{"record":{"id":"a615691c30905f86","repo":"HKUDS/DeepTutor","slug":"a-partner-id-is-required-to-connect-a-partner","errorCode":null,"errorMessage":"A partner_id is required to connect a partner.","messagePattern":"A partner_id is required to connect a partner\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"deeptutor/api/routers/subagents.py","lineNumber":153,"sourceCode":"\n    A partner connection (``agent_kind == \"partner\"``) binds a ``partner_id``\n    instead of a working directory: consulting it opens a fresh session on that\n    partner, exactly as if the user started one from the partner page. Every\n    consult within one DeepTutor chat lands in that one partner session.\n    \"\"\"\n    name = (payload.name or \"\").strip()\n    agent_kind = (payload.agent_kind or \"\").strip()\n    if not name or not agent_kind:\n        raise HTTPException(status_code=400, detail=\"Both name and agent_kind are required.\")\n    if agent_kind not in list_backend_kinds():\n        raise HTTPException(status_code=400, detail=f\"Unknown agent kind: {agent_kind!r}\")\n\n    resolved_cwd = \"\"\n    partner_id = \"\"\n    if agent_kind == PARTNER_BACKEND_KIND:\n        partner_id = (payload.partner_id or \"\").strip()\n        if not partner_id:\n            raise HTTPException(\n                status_code=400, detail=\"A partner_id is required to connect a partner.\"\n            )\n        # Partners are admin-managed, but an admin can assign one to a user via\n        # the grant system. An admin may connect any partner; a non-admin only a\n        # partner assigned to them (403 otherwise). The partner still runs in its\n        # own isolated scope — connecting just lets the user consult it in chat.\n        assert_partner_allowed(partner_id)\n        from deeptutor.services.partners import get_partner_manager\n\n        if not get_partner_manager().partner_exists(partner_id):\n            raise HTTPException(status_code=400, detail=f\"No partner named {partner_id!r}.\")\n    else:\n        raw_cwd = (payload.cwd or \"\").strip()\n        if raw_cwd:\n            try:\n                resolved_cwd = str(assert_path_allowed(raw_cwd))\n            except ValueError as exc:\n                raise HTTPException(status_code=400, detail=str(exc)) from exc","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/api/routers/subagents.py#L135-L171","documentation":"Connections whose agent_kind is the partner backend must include a non-empty partner_id; partners are pre-provisioned and referenced by id, unlike CLI agents.","triggerScenarios":"POST /connections with agent_kind == PARTNER_BACKEND_KIND and partner_id missing, null, or whitespace.","commonSituations":"Reusing the CLI-connection form for partners without a partner selector; partner id copied with trailing spaces; frontend never sends partner_id.","solutions":["Include the partner_id from the partner listing in the payload","Ensure the UI requires partner selection when agent_kind is the partner kind","Verify the partner exists via the partners endpoint before connecting"],"exampleFix":"// before\n{\"name\":\"tutor\",\"agent_kind\":\"partner\"}\n// after\n{\"name\":\"tutor\",\"agent_kind\":\"partner\",\"partner_id\":\"math-buddy\"}","handlingStrategy":"validation","validationCode":"if (agentKind === 'partner' && !partnerId?.trim()) {\n  showPartnerRequired(); return;\n}","typeGuard":"const needsPartnerId = (kind: string) => kind === 'partner';","tryCatchPattern":null,"preventionTips":["Show a required partner select when agent_kind is partner","Default partner_id from the partner list"],"tags":["http-400","partners","subagent","api"],"backgroundTag":"missing-required-field","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}