Repository navigation
Claude Desktop integration cannot delete unavailable models: PUT rejects any change to them #4167
Description
Client or integration
OpenCodex dashboard (Integrations -> Claude tab, clientId=claude-desktop), backed by GET/PUT /api/claude-desktop and POST /api/claude-desktop/apply.
Area
Dashboard
Summary
The Claude Desktop integration page cannot remove models that are no longer available. Stale claudeCode.desktopProfile.assignments accumulate forever: reconcileDesktopProfile only adds new routes and never prunes, while PUT /api/claude-desktop rejects any change touching an unavailable route. There is no supported dashboard path to delete a closed or renamed model. I expected to be able to delete unavailable assignments (or to have a prune action), while still forbidding adding or reassigning unavailable routes.
Reproduction
- Let
claudeCode.desktopProfile.assignmentsaccumulate stale routes (renamed providers such asGMI/*,Opencode_Go/*,opencode-zen/*, old nestedCommandCode/meta/*, retirednative/gpt-5.6-*). In my case: 38 assignments, 21 flaggedavailable=falsebyGET /api/claude-desktop. - In the dashboard Claude tab, or via API, submit a profile that removes the 21 unavailable routes and keeps only the 17 available ones (defaults unchanged, still pointing at available members).
- The request fails; nothing is saved.
- Root cause in code:
PUT /api/claude-desktopinsrc/server/management/agent-settings-routes.tscomparescurrent.profile.assignments[route]vsparsed.assignments[route]for every model withavailable=falseand throws on any difference;reconcileDesktopProfileinsrc/claude/desktop-profile.tsonly appends missing available models and preserves all stored assignments.
Workaround used (manual, disruptive): back up ~/.opencodex/config.json and the Claude-3p library profile, edit claudeCode.desktopProfile.assignments on disk to keep only the available routes, ocx restart (required because a direct file edit while the proxy runs gets overwritten by the in-memory config on the next apply), then POST /api/claude-desktop/apply, then fully quit and reopen Claude Desktop (it only reads the profile at launch). Verified: GET /api/claude-desktop went from models:38 unavailable:21 to models:17 unavailable:0; library inferenceModels 16 -> 17 (including newly appeared opencode-go/glm-5.3-flash).
Version
2.49.0
Operating system
macOS 26.6.2 (25G83)
Provider and model
Not provider-specific (affects any retired/renamed route wired to Claude Desktop; observed with CommandCode / Stepfun / google-antigravity / nvidia / opencode-go / sensenova / volcengine-agent-plan / xai catalog).
Logs or error output
{"error": "\ud604\uc7ac \uc0ac\uc6a9\ud560 \uc218 \uc5c6\ub294 \ubaa8\ub378\uc740 \uc62e\uae38 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4: CommandCode/deepseek/deepseek-v4-flash-vision-exp"}Redacted configuration
{
"claudeCode.desktopProfile.defaults": {
"opus": "CommandCode/deepseek/deepseek-v4-flash",
"fable": "native/gpt-6-astra",
"sonnet": "CommandCode/meituan/LongCat-2.0:free",
"haiku": "Stepfun/step-3.7-flash"
},
"assignments_before": 38,
"unavailable_before": 21,
"assignments_after": 17,
"unavailable_after": 0
}Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
github-actions commentedon Sep 10, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
lidge-jun commentedon Sep 10, 2026
리뷰 · 우선순위 58 / 80
이 이슈는 OpenCodex 대시보드의 Integrations → Claude 탭(
clientId=claude-desktop)에서, 카탈로그에 더 이상 없는(이름을 바꾸거나 사라진) 모델 배정을 지우지 못하는 버그를 말합니다. 지금devHEAD(c15a98caa) 기준으로도 그대로입니다.GET/PUT /api/claude-desktop과POST /api/claude-desktop/apply가 이 화면의 뒷단입니다. 리포터는 배정이 38개까지 쌓였고 그중 21개가available=false인데, 사용 가능한 17개만 남기려고 PUT을 보내면 저장이 거절됩니다. 에러 문장도 코드와 일치합니다: 「현재 사용할 수 없는 모델은 옮길 수 없습니다: …」.왜 이렇게 되는지는 두 군데가 같이 막기 때문입니다. 첫째,
src/server/management/agent-settings-routes.ts의 PUT 핸들러는buildClaudeDesktopState로 만든 현재 목록에서available=false인 모든 모델에 대해, 저장 전 배정과 요청 본문 배정을JSON.stringify로 비교합니다. 요청에서 그 라우트를 아예 빼서 지우려면after가undefined가 되고, 그건 “옮기기”와 똑같이 차이로 취급되어 400이 납니다. 둘째,src/claude/desktop-profile.ts의reconcileDesktopProfile은 지금 카탈로그에 있는 모델을 배정에 추가만 하고, 예전에 남아 있는 죽은 배정을 지우지는 않습니다. 그래서 GET이available=false로 표시해도, 정상 대시보드 저장 경로로는 그 행을 없앨 방법이 없습니다. 리포터가 말한 기대(없는 모델은 삭제·정리 허용, 없는 모델을 새로 넣거나 가족/기본값으로 옮기는 것만 금지)와 현재 가드의 동작이 어긋납니다.buildClaudeDesktopState(src/server/management/shared.ts)는 살아 있는 카탈로그 라우트 집합과profile.assignments키를 합쳐models[].available을 만듭니다. 즉 UI는 “없다”고 보여 주지만, 저장 가드는 “없는 행의 어떤 변화도 금지”로 읽어 삭제까지 막습니다. 우회는~/.opencodex/config.json을 손으로 고친 뒤ocx restart→POST /api/claude-desktop/apply→ Claude Desktop 완전 종료 후 재실행인데, 프록시가 떠 있는 동안 파일만 고치면 메모리 설정이 다시 덮어씁니다. 재현·우회·파일 경로가 모두 구체적이고, types.ts/config.ts 분리 캠페인과도 겹치지 않는 독립 GUI/통합 버그입니다. 지금dev방향(서비스 가드·Lane B 무료 모델 체인·라운드2 문서 마감)과 직접 충돌하지는 않지만, Claude Desktop 사용자에게는 카탈로그가 바뀔 때마다 배정이 계속 불어나는 실사용 문제입니다.src/server/management/agent-settings-routes.ts 라인 951-956 -
!item.available인 모든 라우트에 대해 before/after가 다르면 무조건 throw한다. 요청에서 키를 빼 삭제하는 경우(after === undefined)도 “옮기기”와 같이 막혀, 대시보드에서 stale assignment를 지울 수 없다.src/server/management/agent-settings-routes.ts 라인 958-963 - unavailable을 새 기본값으로 지정하는 것은 이미 막혀 있다. 이 부분은 유지하는 편이 맞다. 문제는 삭제까지 같은 엄격함으로 묶인 점이다.
src/claude/desktop-profile.ts 라인 171-193 (
reconcileDesktopProfile) - 카탈로그에 있는 모델만 append하고, assignments에만 남은 죽은 라우트는 절대 prune하지 않는다. PUT 가드와 합쳐지면 stale 행이 영구 잔류한다.src/server/management/shared.ts (
buildClaudeDesktopState) -available플래그는 UI/진단용으로 잘 드러나지만, 그 정보를 저장 정책(삭제 허용 vs 재배정 금지)으로 나누지 않는다.대시보드 Claude 탭 / PUT 클라이언트 - unavailable 행에 대한 삭제·정리 UX가 없다. API가 거절하면 사용자는 설정 파일을 직접 고쳐야 한다.
메인테이너의 판단이 필요한 지점
after === undefined만 예외로 둘지, 아니면reconcileDesktopProfile/별도 prune API로 한 번에 청소할지POST /api/claude-desktop/apply) 직전에도 같은 삭제 규칙을 강제할지, 저장된 프로필을 그대로 쓸지너의 추천
이슈를 열고 유지한 채 help wanted로 두되, 수정 PR은 작게 가져가라. PUT 가드를 「unavailable 라우트의 family/alias 변경·추가·기본값 지정은 금지, 키 삭제(
after === undefined)는 허용」으로 바꾸고, 삭제 후 defaults가 그 라우트를 가리키면 같은 family의 available 멤버로 재지정(없으면 null)하라. 이어서reconcileDesktopProfile에 optional prune(또는 대시보드 “unavailable 정리” 액션 → PUT)을 추가해 append-only를 깨지 않으면서도 쌓인 stale을 한 번에 지울 수 있게 하라. types.ts/config.ts 분리와 무관하니 close-don't-rebase 대상이 아니고, 중복 이슈(#1213 등은 카탈로그 모드 UX)와도 합치지 마라. 회귀 테스트: unavailable 행 삭제 PUT 성공, unavailable 행 family 변경 PUT 400, available만 남긴 뒤 GET에서 unavailable=0.이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 11, 2026
Fixed on
devby #4263.The Claude Desktop profile PUT now distinguishes deleting an unavailable route from reassigning one, so stale assignments can be pruned from the dashboard.
Credit to @chilung-cgu, whose commits were carried unchanged into the release chain.
Closing manually: pull requests here target
dev, so GitHub'sCloseslinkage only fires on a merge intomain. This will reach users in the next release.