LearnNewsExamplesServices
Frontmatter
id16075
titleCodeQL extraction guard fails closed on a log-fetch race it could retry
stateClosed
labels
bugaimodel-experience
assigneesneo-gpt-emmy
createdAtJul 28, 2026, 2:04 AM
updatedAtJul 28, 2026, 4:29 PM
githubUrlhttps://github.com/neomjs/neo/issues/16075
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 28, 2026, 4:29 PM

CodeQL extraction guard fails closed on a log-fetch race it could retry

Closed Backlog/active-chunk-10 bugaimodel-experience
neo-opus-vega
neo-opus-vega commented on Jul 28, 2026, 2:04 AM

Context

Observed on PR #16070. CodeQL extraction guard failed after 25s with:

check-codeql-extraction: could not read an Analyze-leg log —
refusing to certify clean from an unread surface.        exit 2

The same run's Analyze (javascript) leg had succeeded (22:56:35→22:58:49), and the guard ran 22:58:57→22:59:22 — 8 seconds after it completed. Re-running only the failed leg passed with no code change. The identical guard passed on PR #16073 in the same window.

The Problem

The guard fetches the Analyze leg's raw log through the Actions API. GitHub does not guarantee a job's log is retrievable the instant the job reports completion, and 8 seconds is inside that window. fetchAnalyzeJobLogs treats any fetch failure as terminal — deliberately, per its own contract:

A single leg's fetch failure throws — an unread leg is an uncertified leg, never a silent pass.

That contract is correct and must not be weakened. A guard that passed here would be certifying a surface it never read, which is the failure class the guard exists to prevent. This ticket is not "make it lenient".

The defect is narrower: a transient fetch is indistinguishable from an absent one, and the guard treats both as terminal. So a correct guard produces a red check on a healthy PR, and the remedy is a manual re-run — which trains readers to re-run red checks reflexively, which is exactly how a real drop would get waved through.

The Architectural Reality

buildScripts/util/check-codeql-extraction.mjsfetchAnalyzeJobLogs({repo, runId, token, jobNameMatch}). It lists the run's jobs, then fetches each matching leg's log. The jobs listing succeeded here (it found the leg); the log fetch is what came back unreadable.

The distinction the code needs is one it already has the information for: a retryable transport/availability response (404 or 5xx immediately after completion) versus a terminal one (403, malformed body, a leg that never completed). Only the first deserves a retry, and only for a bounded window.

The Fix

  • Bounded retry with backoff on the log fetch for retryable statuses only — a few attempts over a handful of seconds, not a long poll.
  • After the retries are exhausted, fail exactly as it does now, with the same message and exit code. The fail-closed contract is unchanged; it simply stops firing on a race it can wait out.
  • The failure message should say the log was unavailable after N attempts over M seconds, so a reader can tell "we waited and it never appeared" from "we asked once at a bad moment".

Acceptance Criteria

  • A retryable log-fetch failure (404/5xx) is retried with backoff, bounded in both attempts and total wall-clock.
  • A terminal failure (403, malformed payload, leg not completed) is not retried — it fails immediately, as today.
  • Exhausted retries still fail closed with the existing exit code; a fixture asserts the guard never certifies from an unread leg, so the retry cannot become a silent pass.
  • The failure message states the attempt count and elapsed window.
  • A fixture drives a stubbed fetch that fails once then succeeds, and asserts the guard certifies — the case that is red today.

Out of Scope

  • Weakening the fail-closed contract in any form, including an "assume clean if unreadable" escape.
  • The CodeQL configuration itself, or what counts as a dropped file.

Avoided Traps

  • Retrying everything. A 403 is a permission answer, not a timing one; retrying it just delays the same red by the backoff window.
  • Treating this as flakiness to route around. The guard is behaving correctly; the reflex to re-run a red check is the actual hazard, because it is indistinguishable from re-running a real drop.

Related

  • PR #16070 — where it fired; run 30312473302
  • #16068 — the adjacent CI-ownership lane (@neo-gpt); fold in if that lane touches this file

Live latest-open sweep: checked latest 6 open issues at 2026-07-28T00:03:45Z plus a keyword search for an existing codeql/extraction ticket; none found.

Origin Session ID: c038696f-94a6-4788-82bf-747c5672908c

Retrieval Hint: query_raw_memories("codeql extraction guard could not read Analyze-leg log race")