{"record":{"id":"d01af22da7774fad","repo":"ZhuLinsen/daily_stock_analysis","slug":"permission-profile-mismatch","errorCode":"permission_profile_mismatch","errorMessage":"App Server did not activate the DSA permission profile","messagePattern":"App Server did not activate the DSA permission profile","errorType":"error_code","errorClass":"CodexAppServerError","httpStatus":null,"severity":"critical","filePath":"src/agent/codex_app_server_transport.py","lineNumber":437,"sourceCode":"            {\n                \"approvalPolicy\": \"never\",\n                \"baseInstructions\": base_instructions,\n                \"cwd\": str(self.safe_cwd),\n                \"developerInstructions\": developer_instructions,\n                \"dynamicTools\": dynamic_tool_specs(self.tool_surface, tool_names),\n                \"environments\": [],\n                \"ephemeral\": True,\n                \"permissions\": PERMISSION_PROFILE,\n                \"runtimeWorkspaceRoots\": [str(self.safe_cwd)],\n            },\n        )\n        thread = result.get(\"thread\") or {}\n        thread_id = thread.get(\"id\")\n        if not isinstance(thread_id, str) or not thread_id:\n            raise CodexAppServerError(\"protocol_error\", \"thread/start did not return a thread id\")\n        active_profile = result.get(\"activePermissionProfile\") or {}\n        if active_profile.get(\"id\") != PERMISSION_PROFILE:\n            raise CodexAppServerError(\n                \"permission_profile_mismatch\",\n                \"App Server did not activate the DSA permission profile\",\n            )\n        with self._state_lock:\n            self._thread_tools[thread_id] = set(tool_names)\n            self._thread_metadata[thread_id] = {\n                \"active_permission_profile\": active_profile,\n                \"approval_policy\": result.get(\"approvalPolicy\"),\n                \"cwd_matches\": result.get(\"cwd\") == str(self.safe_cwd),\n                \"runtime_roots_match\": result.get(\"runtimeWorkspaceRoots\") == [str(self.safe_cwd)],\n                \"sandbox\": result.get(\"sandbox\"),\n            }\n        return thread_id\n\n    def thread_metadata(self, thread_id: str) -> dict:\n        with self._state_lock:\n            return dict(self._thread_metadata.get(thread_id, {}))\n","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/agent/codex_app_server_transport.py#L419-L455","documentation":"start_thread() requests the DSA permission profile (constant PERMISSION_PROFILE = 'dsa_gate_a'). After validating the thread id, it checks result['activePermissionProfile']['id'] equals that profile. A mismatch (different profile id, or activePermissionProfile absent so .get('id') is None) raises code 'permission_profile_mismatch' — the server did not honor the requested sandbox permissions and the thread must not be used.","triggerScenarios":"The installed Codex App Server does not support the named permission profile and silently activates its default; a server bug ignores the 'permissions' field of thread/start; the profile id was renamed in a newer protocol version; a response envelope change moves the field so activePermissionProfile comes back empty.","commonSituations":"Upgrading the codex binary to a version where 'dsa_gate_a' is unknown or renamed; running the transport against a mock that echoes params but not the active profile; enterprise-managed Codex configs forcing a different default profile.","solutions":["Confirm the installed Codex App Server knows the permission profile 'dsa_gate_a' (check its docs/config for supported profile ids)","Pin the codex binary version tested with this transport; upgrade the transport's PERMISSION_PROFILE constant only together with a verified server","Log the raw thread/start result to see what activePermissionProfile (if any) the server returned","Treat this as fail-closed: never proceed with a thread whose effective permissions are unknown"],"exampleFix":"# before: transport requests profile 'dsa_gate_a'\nresult = client.request(\"thread/start\", {..., \"permissions\": \"dsa_gate_a\"})\n# server returns {\"activePermissionProfile\": {\"id\": \"default\"}} -> mismatch\n\n# after: align server and transport on one profile id\n# server side: activate the requested profile verbatim\n# transport side (after verified protocol upgrade):\n# PERMISSION_PROFILE = \"dsa_gate_v2\"  # only with a matching server","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def profile_activated(result: dict, profile: str = \"dsa_gate_a\") -> bool:\n    active = result.get(\"activePermissionProfile\") or {}\n    return isinstance(active, dict) and active.get(\"id\") == profile","tryCatchPattern":"try:\n    thread_id = client.start_thread(...)\nexcept CodexAppServerError as exc:\n    if exc.code == \"permission_profile_mismatch\":\n        fail_closed(\"server refused DSA permission profile; do not run with unknown permissions\")\n    raise","preventionTips":["Fail closed: never proceed with a thread whose active permissions are unverified","Pin the codex binary version that supports the dsa_gate_a profile","Add the permission-profile assertion to preflight smoke tests in CI"],"tags":["codex","permissions","sandbox","security","protocol"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}