{"record":{"id":"5fb60274f7e11bb2","repo":"can1357/oh-my-pi","slug":"workspace-key-does-not-match-repo","errorCode":null,"errorMessage":"workspace_key does not match repo","messagePattern":"workspace_key does not match repo","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"python/robomp/src/proxy/server.py","lineNumber":917,"sourceCode":"                remote_url=remote.url,\n                auth_url=remote.auth_url,\n            )\n        except GitCommandError as exc:\n            return _git_error_response(exc)\n        return JSONResponse({\"pool_dir\": str(target)})\n\n    @app.post(\"/gh/v1/git/push\")\n    async def git_push_endpoint(request: Request) -> JSONResponse:\n        data = await _json_body(request)\n        repo = _require_str(data.get(\"repo\"), \"repo\")\n        workspace_key = _require_str(data.get(\"workspace_key\"), \"workspace_key\")\n        branch = _require_branch(data.get(\"branch\"))\n        expected_head = _require_str(data.get(\"expected_head\"), \"expected_head\")\n        slot_uid = _optional_slot_uid(data.get(\"slot_uid\"))\n        # Sanity-check workspace_key matches the repo claim.\n        expected_prefix = repo.replace(\"/\", \"__\") + \"__\"\n        if not workspace_key.startswith(expected_prefix):\n            raise HTTPException(400, \"workspace_key does not match repo\")\n        repo_dir = _workspace_repo_dir(settings, workspace_key)\n        if not repo_dir.is_dir():\n            raise HTTPException(404, f\"workspace not found: {workspace_key}\")\n        remote = await asyncio.to_thread(\n            _origin_remote_auth,\n            repo_dir,\n            repo,\n            _resolve_token(settings),\n            push=True,\n            slot_uid=slot_uid,\n        )\n        try:\n            result = await _run_git_op(\n                git_push,\n                repo_dir,\n                branch=branch,\n                expected_head=expected_head,\n                token=remote.token,","sourceCodeStart":899,"sourceCodeEnd":935,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy/server.py#L899-L935","documentation":"The `git_push_endpoint` validates that the caller-supplied `workspace_key` starts with `<owner>__<repo>__` — the naming scheme the proxy uses to bind workspaces to repositories. If the key does not match the `repo` field in the same request, the proxy rejects it with HTTP 400 to prevent pushing a workspace's repo to a different GitHub target.","triggerScenarios":"POSTing to /gh/v1/git/push with a `repo` like `owner/name` but a `workspace_key` that does not begin with `owner__name__` — e.g. a workspace key belonging to another repo, a typo'd repo field, or a hand-constructed workspace_key.","commonSituations":"Copy-pasting a workspace_key from one task/session into a push request for a different repo, renaming/moving the repo (owner transfer) while workspace keys keep the old prefix, or constructing keys manually instead of from the workspace creation response.","solutions":["Use the workspace_key exactly as returned when the workspace was created, and set `repo` to the repo that workspace belongs to","Check that `repo.replace('/', '__') + '__'` is a prefix of your workspace_key; fix whichever field is wrong","If the repo was renamed/transferred, create a new workspace for the new repo identity"],"exampleFix":"// before\n{ \"repo\": \"owner/other-repo\", \"workspace_key\": \"owner__name__task1\" }\n// after\n{ \"repo\": \"owner/name\", \"workspace_key\": \"owner__name__task1\" }","handlingStrategy":"validation","validationCode":"def check_workspace_matches(repo: str, workspace_key: str) -> None:\n    prefix = repo.replace(\"/\", \"__\") + \"__\"\n    if not workspace_key.startswith(prefix):\n        raise ValueError(f\"workspace_key {workspace_key!r} does not belong to repo {repo!r}\")\ncheck_workspace_matches(repo, workspace_key)","typeGuard":null,"tryCatchPattern":"try:\n    proxy.git_push(repo=repo, workspace_key=key, ...)\nexcept HTTPError as e:\n    if e.response.status_code == 400 and \"workspace_key does not match repo\" in e.response.text:\n        raise ValueError(f\"workspace {key} is not bound to {repo}; use the key returned at workspace creation\") from e\n    raise","preventionTips":["Always take workspace_key from the workspace-creation response, never hand-build it","Keep repo and workspace_key in one config object so they cannot drift apart","Re-derive workspaces after repo renames/transfers","Add a prefix check in client code before sending push requests"],"tags":["validation","http-400","workspace","parameter-mismatch"],"backgroundTag":"workspace-key-repo-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}