Skip to content

[spark-compete] fix(codex): preserve operator next-move when codex binary is missing - #295

Open
4gjnbzb4zf-sudo wants to merge 1 commit into
vibeforge1111:masterfrom
4gjnbzb4zf-sudo:sentinel/ux-friction/codex-missing-binary-clear-error
Open

[spark-compete] fix(codex): preserve operator next-move when codex binary is missing#295
4gjnbzb4zf-sudo wants to merge 1 commit into
vibeforge1111:masterfrom
4gjnbzb4zf-sudo:sentinel/ux-friction/codex-missing-binary-clear-error

Conversation

@4gjnbzb4zf-sudo

@4gjnbzb4zf-sudo 4gjnbzb4zf-sudo commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

{
"schema": "spark-compete-hotfix-v1",
"event": "spark-compete-first-event",
"submission_mode": "public_repo_pr",
"submission_target_url": "#295",
"team": {
"name": "SparkThisUp",
"members": [
"ValHallaBuilder",
"Baz707",
"DanFireDash"
],
"github_accounts": [
"4gjnbzb4zf-sudo"
],
"llm_device_holder": "ValHallaBuilder",
"device_holder_github": "4gjnbzb4zf-sudo"
},
"target_repo": {
"id": "vibeforge1111/spark-character",
"source": "https://github.com/vibeforge1111/spark-character",
"owner_surface": "spark-character"
},
"issue": {
"type": "usage_friction",
"severity": "low",
"title": "call_codex surfaces raw FileNotFoundError stack trace when codex binary is missing instead of an actionable RuntimeError",
"actual_behavior": "src/spark_character/codex_provider.py::call_codex invokes subprocess.run([s.binary, ...]) without wrapping the call. When the codex binary is missing from PATH (or CODEX_PATH points at a non-existent file), subprocess raises raw FileNotFoundError with a generic message that does not name the binary path or the CODEX_PATH override. The companion codex_available() probe path at line 93+ already catches the same exception class locally and returns False -- so the asymmetry exists on the call_codex hot path only.",
"expected_behavior": "call_codex catches FileNotFoundError around the codex subprocess.run call and re-raises as RuntimeError naming the binary path it tried and the CODEX_PATH env-var override (the two remediation surfaces the operator has). The codex_available probe path already swallows the same exception, so the asymmetry is closed without behavior change on successful exec.",
"repro_steps": [
"gh pr checkout ",
"Unset CODEX_PATH and ensure no codex binary is on PATH.",
"Trigger any spark-character path that calls call_codex (e.g. pipeline.generate with codex backend).",
"BEFORE: raw FileNotFoundError stack trace with [Errno 2] No such file or directory: 'codex'. AFTER: RuntimeError naming the binary path attempted and the CODEX_PATH override path."
],
"affected_workflow": "Operators onboarding spark-character with the codex backend (or switching providers) hit this when codex is not installed. The previous raw FileNotFoundError stack trace gave no path to inspect and no remediation; the new RuntimeError names both surfaces the operator can adjust."
},
"evidence": {
"safe_links_only": true,
"before_after_proof": "Single-file change in src/spark_character/codex_provider.py. The subprocess.run call inside call_codex is wrapped with try/except FileNotFoundError that re-raises as RuntimeError naming s.binary and a one-line operator next-step hint (install codex or set CODEX_PATH). Successful exec path unchanged.",
"links": [
"https://github.com//pull/295"
],
"forbidden": [
"pdf",
"zip",
"exe",
"unknown downloads",
"shortened links",
"archives",
"binaries",
"tokens",
"browser cookies",
"wallet material",
"raw logs",
"raw conversations",
"raw memory",
"raw patches",
"private repo maps",
"private scoring details"
]
},
"proposed_fix": {
"approach": "Wrap the subprocess.run call in call_codex with try/except FileNotFoundError. On catch, re-raise RuntimeError with a message that interpolates s.binary and names the CODEX_PATH override path. Same pattern as the existing codex_available() probe at line 93+ that catches the same exception class.",
"files_expected": [
"src/spark_character/codex_provider.py"
],
"tests_or_smoke": "python3 -m py_compile src/spark_character/codex_provider.py -> clean. Manual: run call_codex with no codex binary on PATH; observe the new RuntimeError names the binary + CODEX_PATH. Successful exec path unchanged."
},
"pr": {
"branch": "sentinel/ux-friction/codex-missing-binary-clear-error",
"title_prefix": "[spark-compete]",
"author_github": "4gjnbzb4zf-sudo",
"body_must_include": [
"packet",
"team",
"pr_author",
"repo",
"actual_behavior",
"expected_behavior",
"repro_steps",
"before_after_proof",
"tests_or_smoke",
"duplicate_notes",
"risk_notes",
"review_claim"
],
"url": "#295"
},
"review_claim": {
"impact_claim": "low",
"evidence_types": [
"redacted_terminal_excerpt"
],
"duplicate_notes": "Pre-flight check returned PR #295 area siblings: PR #89/#151/#163/#284 touch different files in spark-character. Sibling PR (this repo) for codex_provider.py out-path missing (different lines + different goal) ships as a separate parallel fix. No exact-line collision.",
"risk_notes": "Local scope: one try/except added around an existing subprocess call. Successful-exec branch unchanged. New RuntimeError uses the same exception class operators already see for other codex errors; no new package.",
"review_state_requested": "pr_review"
}
}

Wraps subprocess.run in call_codex with FileNotFoundError +
TimeoutExpired guards. Prevents a raw OSError stack trace from
surfacing to the eval driver when the codex CLI is not installed
or CODEX_PATH points at a removed binary; preserves the actionable
next step (install codex / set CODEX_PATH) in the RuntimeError text.
Closes the silent-hang window on timeout by surfacing the configured
timeout_seconds in the raised message.
@4gjnbzb4zf-sudo

Copy link
Copy Markdown
Contributor Author

TL;DR — Driving the eval harness with the codex backend on a machine where the codex CLI isn't installed (or where CODEX_PATH points at a removed binary) currently surfaces a raw FileNotFoundError: [Errno 2] No such file or directory: 'codex' from subprocess.run. The operator sees a stack trace, not the next move.

Repro (≤30s):

python3 -c "
from spark_character.codex_provider import call_codex, CodexSpec
spec = CodexSpec(binary='/nonexistent/codex', timeout_seconds=1.0)
call_codex(spec=spec, system_prompt='s', user_prompt='hi')
"

Before: FileNotFoundError: [Errno 2] No such file or directory: '/nonexistent/codex'
After: RuntimeError: codex binary not found at '/nonexistent/codex'. Install the codex CLI or set CODEX_PATH / SPARK_CODEX_PATH to its absolute path.

The probe-path helper codex_available() already filters FileNotFoundError, TimeoutExpired, and PermissionError (lines 100-101) — so the design intent is clearly to catch these. This PR just realigns the actual call path with that intent and also catches TimeoutExpired so a stalled codex CLI surfaces the configured timeout_seconds budget instead of a bare exception.

One-file diff, +25/-6, no behaviour change on the happy path or on the existing rc != 0 / missing-output-file branches.

ifeoluwaaj pushed a commit to ifeoluwaaj/spark-character that referenced this pull request Jun 27, 2026
…t stderr

PR vibeforge1111#295 (adopt): wrap subprocess.run in try/except so a missing codex
binary (FileNotFoundError) or an exceeded budget (TimeoutExpired) surface
as an actionable RuntimeError that preserves the operator's next move,
instead of a raw stack trace.

PR vibeforge1111#296 (adjust): redact the raw stderr from the non-zero-exit RuntimeError
(keep rc for triage, drop the stderr payload that can carry internal paths
or prompt fragments). Per maintainer note, the tautological packet test
(re-implemented the message, never imported codex_provider) was discarded;
replaced with tests/test_codex_exec_stderr.py exercising the real
call_codex redaction path via a monkeypatched subprocess.run.

PR vibeforge1111#295
PR vibeforge1111#296

Co-authored-by: 4gjnbzb4zf-sudo@users.noreply.github.com
Co-authored-by: TALLSOME24@users.noreply.github.com
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant