ZhuLinsen/daily_stock_analysis · critical · CodexAppServerError
permission_profile_mismatch
permission_profile_mismatch
Error message
App Server did not activate the DSA permission profile
What it means
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.
Source
Thrown at src/agent/codex_app_server_transport.py:437
{
"approvalPolicy": "never",
"baseInstructions": base_instructions,
"cwd": str(self.safe_cwd),
"developerInstructions": developer_instructions,
"dynamicTools": dynamic_tool_specs(self.tool_surface, tool_names),
"environments": [],
"ephemeral": True,
"permissions": PERMISSION_PROFILE,
"runtimeWorkspaceRoots": [str(self.safe_cwd)],
},
)
thread = result.get("thread") or {}
thread_id = thread.get("id")
if not isinstance(thread_id, str) or not thread_id:
raise CodexAppServerError("protocol_error", "thread/start did not return a thread id")
active_profile = result.get("activePermissionProfile") or {}
if active_profile.get("id") != PERMISSION_PROFILE:
raise CodexAppServerError(
"permission_profile_mismatch",
"App Server did not activate the DSA permission profile",
)
with self._state_lock:
self._thread_tools[thread_id] = set(tool_names)
self._thread_metadata[thread_id] = {
"active_permission_profile": active_profile,
"approval_policy": result.get("approvalPolicy"),
"cwd_matches": result.get("cwd") == str(self.safe_cwd),
"runtime_roots_match": result.get("runtimeWorkspaceRoots") == [str(self.safe_cwd)],
"sandbox": result.get("sandbox"),
}
return thread_id
def thread_metadata(self, thread_id: str) -> dict:
with self._state_lock:
return dict(self._thread_metadata.get(thread_id, {}))
View on GitHub (pinned to 5159bd72e8)
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
Example fix
# before: transport requests profile 'dsa_gate_a'
result = client.request("thread/start", {..., "permissions": "dsa_gate_a"})
# server returns {"activePermissionProfile": {"id": "default"}} -> mismatch
# after: align server and transport on one profile id
# server side: activate the requested profile verbatim
# transport side (after verified protocol upgrade):
# PERMISSION_PROFILE = "dsa_gate_v2" # only with a matching server Defensive patterns
Strategy: try-catch
Type guard
def profile_activated(result: dict, profile: str = "dsa_gate_a") -> bool:
active = result.get("activePermissionProfile") or {}
return isinstance(active, dict) and active.get("id") == profile Try / catch
try:
thread_id = client.start_thread(...)
except CodexAppServerError as exc:
if exc.code == "permission_profile_mismatch":
fail_closed("server refused DSA permission profile; do not run with unknown permissions")
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/d01af22da7774fad.
Report an issue: GitHub.