--- id: "bug-1754" status: "fixed" status_updated: "2026-09-26" status_note: "6876e4f6: WAL_JOB_FAIL carries an int32 code trailer; both apply seams adopt it into exit_info (non-POST_EXEC stages). Also fixed apply_wal_entry assigning wire state 0 for STAGE_FLAG FAIL entries." component: "controller" severity: "unknown" timestamp: "2026-09-01T13:00:00Z" last_seen: "2026-09-19T09:00:00Z" occurrences: 1 files: ["src/controller/job.c"] tags: ["worker-rejoin-resync", "task-7.1", "job-c", "record_job_fail_fact", "apply_job_fail_transition", "exit_info", "exit_code", "wal_job_fail", "requirement-4", "discovered-not-fixed", "systemic"] related: ["bug-1747"] fix_commits: ["6876e4f6"] legacy: true gitea_issue: 266 gitea_synced_status: "fixed" gitea_synced_hash: "80579d7dca17" --- # bug-1754: Discovered while building worker-rejoin-resync task 7.1's full round-trip integration test… ## Description Discovered while building worker-rejoin-resync task 7.1's full round-trip integration test (test_full_rejoin_reconciliation_round_trip, test_worker_resync.c): a worker-reported exit code for a finished-FAILURE outcome is never durably recorded by ANY existing path in this codebase, reconciliation or ordinary. record_job_fail_fact()'s WAL_JOB_FAIL payload (job.c, persist_submit_job_fact_ex(..., WAL_JOB_FAIL, jobid, 0, WAL_JOB_FAIL_STAGE_FLAG|stage)) carries no exit-code slot at all, and apply_job_fail_transition() (job.c:2831-2853) never touches job.job.exit_info for the FAIL path -- it stays NULL unconditionally for every job failure. The new round-trip test asserts this honestly (TEST_ASSERT_NULL on job.job.exit_info with an inline comment) rather than encoding a wrong 'correct' expectation. ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause Distinct from and broader than bug-1747 (which documents only the FAIL finish-time mistiming, via time(NULL) instead of a worker-reported value). This gap is about the exit CODE, not the finish TIME: WAL_JOB_FAIL's wire payload was never given a code slot in the first place, so no caller of record_job_fail_fact()/on_job_fail() -- worker-reported or otherwise -- has ever been able to persist or apply a job's actual failure exit code. Systemic and pre-existing; unrelated to worker-rejoin-resync's own changes, which only exercise (and correctly do not attempt to fix) this existing gap. ## Fix NOT FIXED -- out of scope for worker-rejoin-resync (a test-only validation task, task 7.1, correctly declined to patch a production defect it incidentally surfaced). Recommend a dedicated future task: extend WAL_JOB_FAIL's wire payload with an exit-code field (mirroring bug-1747's eventual FAIL-side fix for finish-time, if/when that lands) and have apply_job_fail_transition() populate job.job.exit_info from it. Until then, job.job.exit_info is NULL after any job failure, worker-rejoin-resync reconciliation included -- callers/UIs must not assume this field is populated for a failed job. ## Verification _Not recorded (migrated from buglog.json)._ ## References - Related: bug-1747 - Gitea: #266