Summary
With @openai/codex-security@0.1.7, a deep scan can fail before discovery because the generated agent prompt tells the agent to call the MCP tool with the literal string "$CODEX_SECURITY_SCAN_ID" as scanId.
The agent follows that instruction literally in the MCP JSON argument. Environment-variable expansion does not occur in JSON tool arguments, so the workbench rejects it as an invalid UUID. The CLI then reports only the secondary missing-artifacts error, which makes this look like a shell/filesystem permission problem.
Reproduction
Run a deep scan from a clean detached worktree, with the output directory outside the worktree:
git worktree add --detach ../repo-codex-scan HEAD
codex-security scan ../repo-codex-scan \
--auth chatgpt \
--effort high \
--mode deep \
--output-dir /tmp/codex-security-output
In the Codex session transcript, the first deep-scan MCP call is equivalent to:
start_codex_security_deep_scan({"scanId":"$CODEX_SECURITY_SCAN_ID"})
The MCP server responds:
MCP error -32602: Invalid UUID
The agent then stops, and finalization surfaces only:
Could not save the Codex Security scan: Scan agent did not create required draft artifacts:
scan-manifest.json, findings.json, coverage.json.
Expected behavior
The SDK should pass the registered scan's actual UUID to the agent/MCP call, for example:
{"scanId":"c690abe8-fe02-474a-9394-8c7920d3e127"}
If initialization fails, the CLI should also preserve the Invalid UUID MCP error as the primary diagnostic rather than replacing it with the downstream missing-artifacts error.
Root cause
The current source prompt contains:
'The SDK has already registered this scan. Call start_codex_security_deep_scan with { scanId: "$CODEX_SECURITY_SCAN_ID" }; never pass targetPath or create another scan.'
Source on current main:
https://github.com/openai/codex-security/blob/442e5936a217978272fe4b5ff03468e821af4908/sdk/typescript/src/api.ts
This syntax would be expanded by a shell command, but the instructed operation is a structured MCP call. The model therefore sends the string beginning with $ exactly as written.
Suggested fix
After registration, interpolate the already-known scanId into the deep-scan prompt before starting the Codex thread. Alternatively, pass scanId into scanPrompt() and render the UUID there.
A local workaround that replaced the literal placeholder in basePrompt with the registered scanId allowed initialization to proceed. One detail: when false-positive feedback is present, the feedback branch must append to the corrected prompt rather than resetting it back to basePrompt.
It would also help to propagate initialization MCP failures through finalization so that a concrete Invalid UUID error is not masked by the generic required-draft-artifacts check.
Environment
@openai/codex-security@0.1.7
- Bundled plugin:
0.1.15
- Authentication:
--auth chatgpt
- Mode:
--mode deep
- Node.js:
v24.13.0
- macOS:
26.5.2
- Clean detached Git worktree
- Output directory outside the scanned worktree
Summary
With
@openai/codex-security@0.1.7, a deep scan can fail before discovery because the generated agent prompt tells the agent to call the MCP tool with the literal string"$CODEX_SECURITY_SCAN_ID"asscanId.The agent follows that instruction literally in the MCP JSON argument. Environment-variable expansion does not occur in JSON tool arguments, so the workbench rejects it as an invalid UUID. The CLI then reports only the secondary missing-artifacts error, which makes this look like a shell/filesystem permission problem.
Reproduction
Run a deep scan from a clean detached worktree, with the output directory outside the worktree:
In the Codex session transcript, the first deep-scan MCP call is equivalent to:
The MCP server responds:
The agent then stops, and finalization surfaces only:
Expected behavior
The SDK should pass the registered scan's actual UUID to the agent/MCP call, for example:
{"scanId":"c690abe8-fe02-474a-9394-8c7920d3e127"}If initialization fails, the CLI should also preserve the
Invalid UUIDMCP error as the primary diagnostic rather than replacing it with the downstream missing-artifacts error.Root cause
The current source prompt contains:
'The SDK has already registered this scan. Call start_codex_security_deep_scan with { scanId: "$CODEX_SECURITY_SCAN_ID" }; never pass targetPath or create another scan.'Source on current
main:https://github.com/openai/codex-security/blob/442e5936a217978272fe4b5ff03468e821af4908/sdk/typescript/src/api.ts
This syntax would be expanded by a shell command, but the instructed operation is a structured MCP call. The model therefore sends the string beginning with
$exactly as written.Suggested fix
After registration, interpolate the already-known
scanIdinto the deep-scan prompt before starting the Codex thread. Alternatively, passscanIdintoscanPrompt()and render the UUID there.A local workaround that replaced the literal placeholder in
basePromptwith the registeredscanIdallowed initialization to proceed. One detail: when false-positive feedback is present, the feedback branch must append to the corrected prompt rather than resetting it back tobasePrompt.It would also help to propagate initialization MCP failures through finalization so that a concrete
Invalid UUIDerror is not masked by the generic required-draft-artifacts check.Environment
@openai/codex-security@0.1.70.1.15--auth chatgpt--mode deepv24.13.026.5.2