can1357/oh-my-pi · error
Security lineage comparison requires a completed after-scan;
Error message
Security lineage comparison requires a completed after-scan; ${after.scan.id} is ${after.scan.status} What it means
compareSecurityLineage only produces trustworthy 'resolved' claims when the after-scan finished. If the after bundle's scan status is anything other than 'completed' (cancelled, failed, partial), unmatched earlier findings might simply not have been re-scanned, so reporting them as resolved would be false. The function throws with the scan id and actual status.
Source
Thrown at packages/coding-agent/src/security/comparison.ts:217
),
candidateOnlyFindingIds,
referenceFindingCount: reference.findings.length,
candidateFindingCount: candidate.findings.length,
matchedFindingCount: matches.length,
recallAgainstReference: ratio(matches.length, reference.findings.length),
precisionAgainstReference: ratio(matches.length, candidate.findings.length),
jaccardOverlap: ratio(matches.length, unionSize),
};
}
export function compareSecurityLineage(
before: SecurityScanBundle,
after: SecurityScanBundle,
): SecurityComparisonReport {
// An incomplete after-scan proves nothing about unmatched earlier findings;
// claiming them "resolved" against a cancelled/partial/failed run would be a lie.
if (after.scan.status !== "completed") {
throw new Error(
`Security lineage comparison requires a completed after-scan; ${after.scan.id} is ${after.scan.status}`,
);
}
const differential = compareSecurityProducers(before, after);
const beforeById = new Map(before.findings.map(finding => [finding.id, finding]));
const afterById = new Map(after.findings.map(finding => [finding.id, finding]));
const matches: SecurityFindingMatch[] = differential.matches.map(match => {
const beforeFinding = beforeById.get(match.referenceFindingId);
const afterFinding = afterById.get(match.candidateFindingId);
if (!beforeFinding || !afterFinding) throw new Error("Security comparison produced an invalid finding reference");
return {
beforeFindingId: beforeFinding.id,
afterFindingId: afterFinding.id,
fingerprint: beforeFinding.fingerprint,
status: "unchanged",
matchBasis: match.basis,
};
});View on GitHub (pinned to 9690622007)
Solutions
- Wait for the after-scan to reach 'completed' before comparing (poll status or await the scan promise)
- Re-run the security scan to produce a completed bundle
- Check after.scan.status before calling and surface a 'scan incomplete' message in your UI instead
- Compare against a different, previously completed bundle
Example fix
// before
const report = compareSecurityLineage(before, afterMaybeIncomplete);
// after
if (afterMaybeIncomplete.scan.status !== "completed") {
throw new Error(`Re-run scan ${afterMaybeIncomplete.scan.id}; it did not complete.`);
}
const report = compareSecurityLineage(before, afterMaybeIncomplete); Defensive patterns
Strategy: validation
Validate before calling
if (after.scan.status !== "completed") {
throw new Error(`After-scan ${after.scan.id} is ${after.scan.status}; rerun before comparing`);
} Type guard
const isCompleted = (b: SecurityScanBundle): boolean => b.scan.status === "completed";
Try / catch
try {
report = compareSecurityLineage(before, after);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Security lineage comparison requires a completed after-scan")) {
// queue a rescan and retry later; do not report 'resolved' counts
} else throw err;
} Prevention
- Gate comparisons on after.scan.status === "completed" in your pipeline
- Never compare against cancelled/failed/running scans
- Await the scan promise (or poll status) before generating reports
- Treat partially imported bundles as non-comparable
When it happens
Trigger: Calling compareSecurityLineage(before, after) (directly or via compare/report) where after.scan.status is 'cancelled', 'failed', 'partial', 'running', etc.
Common situations: User cancelled a long scan then asked for a before/after report; a scan crashed mid-run and its partial bundle was persisted; comparing against a scan still in progress; importing a stale/incomplete bundle as the 'after' side.
Related errors
- Gemini Files API finalized file state is not ACTIVE
- lookbackDays must be a positive integer or 'all'
- Security comparison produced an invalid finding reference
- Security scan contains duplicate finding ids
- Security scan manifest contains duplicate finding references
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c04ccb545b8a4812.
Report an issue: GitHub.