Summary
On Windows 11 with Codex CLI 0.144.3 and the OMO 4.16.0 marketplace plugin, a normal CLI session showed two unattributed SessionStart failures with hook timed out after 5s, followed by repeated UserPromptSubmit and Stop invalid-JSON failures.
The investigation separated two causes:
- OMO owns a confirmed
SessionStart timeout risk: three Windows handlers still have a 5-second limit. Isolated probes showed telemetry at 9,128 ms and auto-update at 6,077 ms even with their optional work disabled. The historical CLI output did not identify which handlers produced its two timeout notices.
- A co-installed global hook is a confirmed incompatible JSON emitter and is sufficient to reproduce the same Codex classification; current OMO handlers passed the isolated output checks. The original CLI notices were not toggled end to end, so their exact emitter remains unverified. LazyCodex can make this actionable by having
doctor identify incompatible co-installed hooks and report their source path, elapsed time, and sanitized output shape.
Environment
- OS: Windows 11 Pro 10.0.26200, AMD64
- Windows PowerShell: 5.1.26100.8655
- Node.js: 22.15.0
- Codex CLI: 0.144.3, installed through global npm
- Installed OMO marketplace plugin: 4.16.0
- Latest LazyCodex source checked:
3d7416bff3e6c80ebf5542b4dd12f5c76298d46d (OMO 4.17.1)
- Latest upstream Codex source checked:
2e156cbe31212ccfa9173cb2679c1a201a940b18
- Relevant configuration: OMO enabled through the
sisyphuslabs marketplace; global UserPromptSubmit and Stop hook groups also enabled
Repository Decision
- Target repository:
code-yeongyu/lazycodex
- Why this belongs here: the confirmed timeout is defined by OMO hook manifests and persists in current LazyCodex 4.17.1 source. The second actionable gap is LazyCodex hook-health diagnosis: the generic CLI error does not identify which co-installed handler emitted incompatible JSON.
- LazyCodex evidence:
- Upstream Codex evidence:
- Codex reports the handler's configured limit from
command_runner.rs.
UserPromptSubmit and Stop outputs use strict schemas with unknown fields denied in schema.rs.
- Empty stdout is accepted as a no-op, while JSON-looking incompatible output is reported as invalid in
user_prompt_submit.rs and stop.rs.
Reproduction
-
On Windows, enable OMO 4.16.0 in Codex and start a normal CLI session. In the reported session, Codex displayed two unattributed SessionStart hook (failed): hook timed out after 5s notices.
-
Reproduce the OMO timeout risk independently with this sanitized probe. Replace <OMO_PLUGIN_ROOT> with the installed plugin directory:
$PluginRoot = '<OMO_PLUGIN_ROOT>'
$Dispatch = Join-Path $PluginRoot 'components\bootstrap\scripts\node-dispatch.ps1'
$env:CODEX_HOME = Join-Path $env:USERPROFILE '.codex'
$env:PLUGIN_ROOT = $PluginRoot
$env:PLUGIN_DATA = Join-Path $env:CODEX_HOME 'plugins\data\sisyphuslabs\omo'
$env:OMO_CODEX_DISABLE_POSTHOG = '1'
$env:LAZYCODEX_AUTO_UPDATE_DISABLED = '1'
$env:CODEX_CODEGRAPH_ENABLED = '0'
$Payload = '{"session_id":"probe-session","transcript_path":null,"cwd":"C:\\temp","hook_event_name":"SessionStart","model":"gpt-5.5","permission_mode":"never","source":"startup"}'
$Targets = @(
@{ Name = 'telemetry'; File = 'components\telemetry\dist\cli.js' },
@{ Name = 'auto-update'; File = 'scripts\auto-update.mjs' },
@{ Name = 'codegraph'; File = 'components\codegraph\dist\cli.js' }
)
foreach ($Target in $Targets) {
1..5 | ForEach-Object {
$Elapsed = Measure-Command {
$Payload | powershell.exe -NoProfile -ExecutionPolicy Bypass -File $Dispatch (Join-Path $PluginRoot $Target.File) hook session-start
}
[pscustomobject]@{ Handler = $Target.Name; Run = $_; ElapsedMs = [math]::Round($Elapsed.TotalMilliseconds) }
}
}
These controls are present in current source: telemetry opt-out, auto-update disable, and the Codex CodeGraph disable case. Elapsed time includes the nested powershell.exe launcher because that is the real commandWindows path Codex executes.
-
Treat the first invocation in a fresh parent shell as the cold sample and immediate repeats as warm samples. The timeout risk is reproduced when any handler takes more than its 5,000 ms manifest limit.
-
Reproduce the separate attribution gap in a disposable project. This creates a hook that drains stdin, exits 0, and writes only incompatible application status JSON:
$ProbeRoot = Join-Path $env:TEMP 'codex-invalid-hook-output-probe'
$DotCodex = Join-Path $ProbeRoot '.codex'
$HookScript = Join-Path $ProbeRoot 'invalid-hook.mjs'
New-Item -ItemType Directory -Force -Path $DotCodex | Out-Null
@'
process.stdin.resume();
process.stdin.on("end", () => {
process.stdout.write(`${JSON.stringify({ ok: true, skipped: "clean_event" })}\n`);
});
'@ | Set-Content -Encoding ascii -Path $HookScript
$Command = 'node "' + $HookScript + '"'
$Handler = @{ type = 'command'; command = $Command; commandWindows = $Command; timeout = 5 }
$Hooks = @{
hooks = @{
UserPromptSubmit = @(@{ hooks = @($Handler) })
Stop = @(@{ hooks = @($Handler) })
}
}
$Hooks | ConvertTo-Json -Depth 8 | Set-Content -Encoding ascii -Path (Join-Path $DotCodex 'hooks.json')
-
Run codex -C $ProbeRoot, approve the two disposable project hooks when prompted, submit hi, and let the turn finish. Codex reports hook returned invalid user prompt submit JSON output and hook returned invalid stop hook JSON output, but the visible failures do not identify the emitting file.
-
Remove the disposable reproduction after the check:
Remove-Item -LiteralPath $ProbeRoot -Recurse -Force
Expected Behavior
- OMO
SessionStart handlers should reliably finish within their configured timeout on a cold Windows start, including the PowerShell launcher overhead.
- LazyCodex diagnostics should identify the exact active hook source that exceeds its timeout or emits incompatible JSON, without exposing the prompt or raw hook payload.
- A no-op capture/telemetry hook should either emit no stdout or emit a valid event-specific Codex hook output shape.
Actual Behavior
- The reported CLI session showed two unattributed
SessionStart timeouts. Independent OMO probes proved that the telemetry and auto-update handlers can each exceed their configured 5-second limit; they do not prove which handlers emitted the historical notices.
- A separate global hook was confirmed to emit incompatible status JSON for both events and reproduces the same generic invalid-JSON classification. End-to-end attribution of the original notices remains unverified.
- The visible error did not name the handler, so OMO and non-OMO hooks initially appeared to be one failure.
Evidence
Safe synthetic Windows probes used the installed OMO 4.16.0 commandWindows launcher while disabling optional external work:
| Handler |
Cold elapsed |
Configured timeout |
Result |
| Telemetry |
9,128 ms |
5,000 ms |
Would time out |
| Auto-update |
6,077 ms |
5,000 ms |
Would time out |
| CodeGraph |
4,454 ms |
5,000 ms |
Passed with little headroom |
A warm second pass through the same launcher completed in 2,651-2,735 ms, while the first direct telemetry process in a separate pass still took 5,827 ms. The failure is therefore cold-start sensitive.
Synthetic output checks found:
- OMO rules, ultrawork, ulw-loop, and start-work-continuation no-op paths: exit 0 with empty stdout, accepted by current Codex.
- OMO triggered output builders: valid event-specific JSON shapes in source and tests.
- Co-installed global capture hook: exit 0 with
ok,event,skipped status JSON for both UserPromptSubmit and Stop; rejected because those fields are outside the Codex hook output schemas.
- Co-installed final-handoff hook: valid
decision,reason output; not the invalid Stop emitter.
No exact open LazyCodex duplicate was found. Closed #57 is the closest JSON-output precedent but addressed a prior CodeGraph SessionStart output defect. Closed #45, #80, and #92 cover missing hook payloads or earlier doctor/layout defects rather than this timeout headroom and handler-attribution case.
Root Cause
Insufficient OMO timeout headroom on Windows is confirmed independently of the historical handler attribution. The manifests allow 5 seconds, but cold PowerShell + Node startup and handler initialization can exceed 5 seconds before optional work matters. Current LazyCodex 4.17.1 retains the same limits. The exact two handlers behind the original CLI notices remain unidentified because the visible output did not include handler names.
An incompatible JSON emitter is also confirmed, but isolated current OMO handlers did not emit it. A co-installed global hook prints internal status JSON to stdout, and that output is sufficient to reproduce Codex's invalid-output classification because its top-level fields are outside the hook schema. The original notices were not verified with a disable/re-enable toggle. The LazyCodex-owned gap is diagnosis and attribution, not the external hook's output implementation.
Proposed Fix
- As the minimal immediate fix, raise the telemetry, auto-update, and CodeGraph
SessionStart manifest limits from 5 to 15 seconds in their hook fragments, then add deterministic tests that assert the generated marketplace manifests retain those values.
- Add an opt-in Windows smoke check that runs each real
commandWindows path from a fresh shell and records cold and warm elapsed times. Keep wall-clock thresholds out of the deterministic unit suite.
- Extend
lazycodex-ai doctor --platform=codex to inventory global plus plugin hooks and run redacted synthetic payload probes. Report handler source, exit code, elapsed time versus configured timeout, stdout byte count, and parsed top-level keys only.
- Validate synthetic stdout against the event-specific Codex hook schema. Recommend empty stdout for no-op handlers and explain incompatible keys without printing raw payloads.
- Add a regression fixture where a co-installed hook emits
{"ok":true} so doctor attributes the invalid output to that file instead of implying OMO emitted it. Moving nonessential startup work behind a detached path can be evaluated separately from the minimal timeout correction.
Verification Plan
- On a cold Windows test host, run all OMO
SessionStart commandWindows handlers repeatedly and confirm no run reaches its configured timeout.
- Run
doctor with a synthetic incompatible global hook and confirm it names that hook, reports only sanitized output metadata, and returns a clear remediation.
- Confirm OMO no-op and triggered outputs pass current Codex
UserPromptSubmit, Stop, and SessionStart schemas.
- Start Codex with OMO plus the fixed global hook and confirm startup, prompt submission, and stop complete without hook failure notices.
This issue or PR was generated by LazyCodex.
Tag: lazycodex-generated
Summary
On Windows 11 with Codex CLI 0.144.3 and the OMO 4.16.0 marketplace plugin, a normal CLI session showed two unattributed
SessionStartfailures withhook timed out after 5s, followed by repeatedUserPromptSubmitandStopinvalid-JSON failures.The investigation separated two causes:
SessionStarttimeout risk: three Windows handlers still have a 5-second limit. Isolated probes showed telemetry at 9,128 ms and auto-update at 6,077 ms even with their optional work disabled. The historical CLI output did not identify which handlers produced its two timeout notices.doctoridentify incompatible co-installed hooks and report their source path, elapsed time, and sanitized output shape.Environment
3d7416bff3e6c80ebf5542b4dd12f5c76298d46d(OMO 4.17.1)2e156cbe31212ccfa9173cb2679c1a201a940b18sisyphuslabsmarketplace; globalUserPromptSubmitandStophook groups also enabledRepository Decision
code-yeongyu/lazycodexnode-dispatch.ps1, adding a PowerShell process and Node-resolution work before the target handler starts.command_runner.rs.UserPromptSubmitandStopoutputs use strict schemas with unknown fields denied inschema.rs.user_prompt_submit.rsandstop.rs.Reproduction
On Windows, enable OMO 4.16.0 in Codex and start a normal CLI session. In the reported session, Codex displayed two unattributed
SessionStart hook (failed): hook timed out after 5snotices.Reproduce the OMO timeout risk independently with this sanitized probe. Replace
<OMO_PLUGIN_ROOT>with the installed plugin directory:These controls are present in current source: telemetry opt-out, auto-update disable, and the Codex CodeGraph disable case. Elapsed time includes the nested
powershell.exelauncher because that is the realcommandWindowspath Codex executes.Treat the first invocation in a fresh parent shell as the cold sample and immediate repeats as warm samples. The timeout risk is reproduced when any handler takes more than its 5,000 ms manifest limit.
Reproduce the separate attribution gap in a disposable project. This creates a hook that drains stdin, exits 0, and writes only incompatible application status JSON:
Run
codex -C $ProbeRoot, approve the two disposable project hooks when prompted, submithi, and let the turn finish. Codex reportshook returned invalid user prompt submit JSON outputandhook returned invalid stop hook JSON output, but the visible failures do not identify the emitting file.Remove the disposable reproduction after the check:
Expected Behavior
SessionStarthandlers should reliably finish within their configured timeout on a cold Windows start, including the PowerShell launcher overhead.Actual Behavior
SessionStarttimeouts. Independent OMO probes proved that the telemetry and auto-update handlers can each exceed their configured 5-second limit; they do not prove which handlers emitted the historical notices.Evidence
Safe synthetic Windows probes used the installed OMO 4.16.0
commandWindowslauncher while disabling optional external work:A warm second pass through the same launcher completed in 2,651-2,735 ms, while the first direct telemetry process in a separate pass still took 5,827 ms. The failure is therefore cold-start sensitive.
Synthetic output checks found:
ok,event,skippedstatus JSON for bothUserPromptSubmitandStop; rejected because those fields are outside the Codex hook output schemas.decision,reasonoutput; not the invalidStopemitter.No exact open LazyCodex duplicate was found. Closed #57 is the closest JSON-output precedent but addressed a prior CodeGraph
SessionStartoutput defect. Closed #45, #80, and #92 cover missing hook payloads or earlier doctor/layout defects rather than this timeout headroom and handler-attribution case.Root Cause
Insufficient OMO timeout headroom on Windows is confirmed independently of the historical handler attribution. The manifests allow 5 seconds, but cold PowerShell + Node startup and handler initialization can exceed 5 seconds before optional work matters. Current LazyCodex 4.17.1 retains the same limits. The exact two handlers behind the original CLI notices remain unidentified because the visible output did not include handler names.
An incompatible JSON emitter is also confirmed, but isolated current OMO handlers did not emit it. A co-installed global hook prints internal status JSON to stdout, and that output is sufficient to reproduce Codex's invalid-output classification because its top-level fields are outside the hook schema. The original notices were not verified with a disable/re-enable toggle. The LazyCodex-owned gap is diagnosis and attribution, not the external hook's output implementation.
Proposed Fix
SessionStartmanifest limits from 5 to 15 seconds in their hook fragments, then add deterministic tests that assert the generated marketplace manifests retain those values.commandWindowspath from a fresh shell and records cold and warm elapsed times. Keep wall-clock thresholds out of the deterministic unit suite.lazycodex-ai doctor --platform=codexto inventory global plus plugin hooks and run redacted synthetic payload probes. Report handler source, exit code, elapsed time versus configured timeout, stdout byte count, and parsed top-level keys only.{"ok":true}so doctor attributes the invalid output to that file instead of implying OMO emitted it. Moving nonessential startup work behind a detached path can be evaluated separately from the minimal timeout correction.Verification Plan
SessionStartcommandWindowshandlers repeatedly and confirm no run reaches its configured timeout.doctorwith a synthetic incompatible global hook and confirm it names that hook, reports only sanitized output metadata, and returns a clear remediation.UserPromptSubmit,Stop, andSessionStartschemas.This issue or PR was generated by LazyCodex.
Tag: lazycodex-generated