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

  1. Wait for the after-scan to reach 'completed' before comparing (poll status or await the scan promise)
  2. Re-run the security scan to produce a completed bundle
  3. Check after.scan.status before calling and surface a 'scan incomplete' message in your UI instead
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c04ccb545b8a4812. Report an issue: GitHub.