{"record":{"id":"edd5d3012904b05e","repo":"ZhuLinsen/daily_stock_analysis","slug":"capability-unsupported-edd5d3","errorCode":"capability_unsupported","errorMessage":"No cancellation-safe DSA tools are available to Codex","messagePattern":"No cancellation-safe DSA tools are available to Codex","errorType":"error_code","errorClass":"CodexAppServerError","httpStatus":null,"severity":"error","filePath":"src/agent/codex_agent_backend.py","lineNumber":152,"sourceCode":"                command,\n                tool_surface=self.tool_surface,\n                tool_context=tool_context,\n                request_timeout=remaining_timeout(),\n                tool_event_callback=on_tool_event,\n                deadline=deadline,\n                cancel_event=request.cancel_event,\n                max_tool_calls=request.max_steps,\n            ) as client:\n                client.request_timeout = remaining_timeout()\n                tool_names = [\n                    item[\"name\"]\n                    for item in self.tool_surface.list_tools(\n                        \"public\",\n                        cancellation_safe_only=True,\n                    )\n                ]\n                if not tool_names:\n                    raise CodexAppServerError(\n                        \"capability_unsupported\",\n                        \"No cancellation-safe DSA tools are available to Codex\",\n                    )\n                developer_instructions = request.system_prompt\n                if request.stock_scope is None:\n                    developer_instructions = (\n                        f\"{developer_instructions}\\n\\n{_NO_STOCK_SCOPE_INSTRUCTION}\"\n                    )\n                thread_id = client.start_thread(\n                    tool_names=tool_names,\n                    base_instructions=_BASE_INSTRUCTIONS,\n                    developer_instructions=developer_instructions,\n                )\n                client.request_timeout = remaining_timeout()\n                isolation = client.inspect_external_tool_isolation(thread_id)\n                if not isolation.get(\"passed\"):\n                    raise CodexAppServerError(\n                        \"capability_unsupported\",","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/agent/codex_agent_backend.py#L134-L170","documentation":"Raised by CodexAgentBackend right before spawning a Codex App Server thread when ToolSurface.list_tools('public', cancellation_safe_only=True) returns an empty list. The Codex integration only exposes tools whose ToolPolicy.cancellation_safe flag is True, because a user cancellation must be able to interrupt the turn safely. If the registry is empty or every registered tool is marked cancellation-unsafe, the backend refuses to start the session with code 'capability_unsupported'.","triggerScenarios":"Calling the Codex agent backend when the ToolRegistry contains no tools, or when every ToolDefinition has policy.cancellation_safe=False; also triggered when ToolSurface.empty() (the Phase 6a protocol-only surface backed by an empty ToolRegistry) is wired into a production backend instead of a populated registry.","commonSituations":"A new deployment forgets to run tool registration at startup; a refactor removes the only cancellation-safe tool; a test harness uses ToolSurface.empty() outside preflight tests; policy defaults were changed so tools default to cancellation_safe=False.","solutions":["Register at least one tool whose ToolPolicy sets cancellation_safe=True before invoking the Codex backend","Inspect the registry at startup: assert tool_surface.list_tools('public', cancellation_safe_only=True) is non-empty and fail fast with a clear provisioning error","If you constructed ToolSurface.empty(), replace it with ToolSurface(registry) built from your real ToolRegistry","Audit recent changes to tool policy definitions to see whether cancellation_safe was inadvertently flipped to False"],"exampleFix":"// before\nsurface = ToolSurface.empty()  # protocol-only, no tools registered\nbackend = CodexAgentBackend(tool_surface=surface)\nawait backend.run(request)  # raises capability_unsupported\n\n// after\nregistry = build_default_registry()  # registers DSA tools\nsurface = ToolSurface(registry)\nif not surface.list_tools('public', cancellation_safe_only=True):\n    raise RuntimeError('no cancellation-safe tools registered; check tool policies')\nbackend = CodexAgentBackend(tool_surface=surface)","handlingStrategy":"validation","validationCode":"safe = surface.list_tools(\"public\", cancellation_safe_only=True)\nif not safe:\n    raise ProvisioningError(\n        \"no cancellation-safe tools registered; register tools with \"\n        \"ToolPolicy(cancellation_safe=True) before starting the Codex backend\"\n    )\nbackend = CodexAgentBackend(tool_surface=surface)","typeGuard":"def has_cancellation_safe_tools(surface: ToolSurface) -> bool:\n    return bool(surface.list_tools(\"public\", cancellation_safe_only=True))","tryCatchPattern":"try:\n    await backend.run(request)\nexcept CodexAppServerError as exc:\n    if exc.code == \"capability_unsupported\":\n        fail_fast(\"tool provisioning defect: \" + str(exc))\n    raise","preventionTips":["Add a startup assertion that the registry contains at least one cancellation-safe tool","Never wire ToolSurface.empty() into a production backend; reserve it for protocol preflight tests","Cover tool policies with unit tests so cancellation_safe flags cannot silently regress"],"tags":["codex","tool-registry","capability","configuration"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}