can1357/oh-my-pi · error
Security comparison produced an invalid finding reference
Error message
Security comparison produced an invalid finding reference
What it means
compareSecurityLineage maps the producer-differential's matches back to actual finding objects by id. If a match references a finding id absent from either bundle's findings array, the differential produced an internally inconsistent pairing; the function throws instead of emitting a match with dangling references.
Source
Thrown at packages/coding-agent/src/security/comparison.ts:227
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,
};
});
for (const findingId of differential.referenceOnlyFindingIds) {
const finding = beforeById.get(findingId);
if (!finding) continue;
matches.push({ beforeFindingId: finding.id, fingerprint: finding.fingerprint, status: "resolved" });
}
for (const findingId of differential.candidateOnlyFindingIds) {
const finding = afterById.get(findingId);
if (!finding) continue;
matches.push({ afterFindingId: finding.id, fingerprint: finding.fingerprint, status: "new" });
}View on GitHub (pinned to 9690622007)
Solutions
- Pass the complete, unmodified bundles (as produced by readBundle/parseSecurityScanBundle) to compareSecurityLineage
- Regenerate the comparison from freshly read bundles instead of reusing partial in-memory copies
- Verify finding ids were not rewritten between the two scans (fingerprints should be stable)
- If reproducible with intact bundles, report the upstream differential bug
Example fix
// before
const filtered = { ...after, findings: after.findings.filter(f => f.severity === "high") };
const report = compareSecurityLineage(before, filtered);
// after
const report = compareSecurityLineage(before, after); // compare full bundles, filter the report Defensive patterns
Strategy: validation
Validate before calling
const bIds = new Set(before.findings.map(f => f.id));
const aIds = new Set(after.findings.map(f => f.id));
if (before.scan.findingIds.some(id => !bIds.has(id)) || after.scan.findingIds.some(id => !aIds.has(id))) {
throw new Error("Bundle inconsistent; re-read bundles before comparing");
} Try / catch
try {
report = compareSecurityLineage(before, after);
} catch (err) {
if (err instanceof Error && err.message === "Security comparison produced an invalid finding reference") {
// reload bundles from disk and retry the comparison once
} else throw err;
} Prevention
- Pass bundles exactly as parsed — never filter findings without regenerating the comparison inputs
- Keep finding ids and fingerprints stable across scans
- Avoid hand-assembling bundles in memory
- Re-read bundles from the store instead of reusing mutated copies
When it happens
Trigger: A before/after bundle pair where compareSecurityProducers returns a match whose referenceFindingId or candidateFindingId is not present in the respective bundle's findings list — e.g. hand-assembled or partially filtered bundles passed as before/after.
Common situations: Building bundles programmatically and filtering findings without updating the differential inputs; external tooling rewriting finding ids between scans (breaking fingerprint/id pairing); a bug or version skew between the producer that generated matches and the bundle serializer.
Related errors
- Cache benchmark prefix template is missing its raw prefix pl
- Sloppy edit completed without a result.
- pr://${repo}/${parsed.number}/diff/${index} resolved to a mi
- Invalid issue:// URL: unexpected variant '${parsed.kind}'
- RPC message pagination ended before the advertised total
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d218baa66473d405.
Report an issue: GitHub.