can1357/oh-my-pi · error

Security scan omits finding from manifest: ${findingId}

Error message

Security scan omits finding from manifest: ${findingId}

What it means

The inverse check: every finding in the findings array must be listed in scan.findingIds. A finding omitted from the manifest would be invisible to consumers that trust the manifest index, so parseSecurityScanBundle throws with the orphaned finding id.

Source

Thrown at packages/coding-agent/src/security/contracts/validation.ts:46

}

export function parseSecurityScanBundle(value: unknown): SecurityScanBundle {
	const { securityScanBundleSchema } = getSecurityContractSchemas();
	const result = securityScanBundleSchema(value);
	if (result instanceof type.errors) throw schemaError("Security scan bundle", result);
	const bundle = result as SecurityScanBundle;
	const findingIds = new Set(bundle.findings.map(finding => finding.id));
	if (findingIds.size !== bundle.findings.length) throw new Error("Security scan contains duplicate finding ids");
	const referencedFindingIds = new Set(bundle.scan.findingIds);
	if (referencedFindingIds.size !== bundle.scan.findingIds.length) {
		throw new Error("Security scan manifest contains duplicate finding references");
	}
	for (const findingId of referencedFindingIds) {
		if (!findingIds.has(findingId)) throw new Error(`Security scan references missing finding: ${findingId}`);
	}
	for (const findingId of findingIds) {
		if (!referencedFindingIds.has(findingId))
			throw new Error(`Security scan omits finding from manifest: ${findingId}`);
	}
	for (const finding of bundle.findings) {
		if (finding.scanId !== bundle.scan.id) {
			throw new Error(`Finding ${finding.id} belongs to ${finding.scanId}, expected ${bundle.scan.id}`);
		}
		const evidenceIds = new Set(finding.evidence.map(evidence => evidence.id));
		if (evidenceIds.size !== finding.evidence.length) {
			throw new Error(`Finding ${finding.id} contains duplicate evidence ids`);
		}
		const occurrenceIds = new Set(finding.occurrences.map(occurrence => occurrence.id));
		if (occurrenceIds.size !== finding.occurrences.length) {
			throw new Error(`Finding ${finding.id} contains duplicate occurrence ids`);
		}
		for (const occurrence of finding.occurrences) {
			for (const evidenceId of occurrence.evidenceIds) {
				if (!evidenceIds.has(evidenceId)) {
					throw new Error(`Occurrence ${occurrence.id} references missing evidence: ${evidenceId}`);
				}

View on GitHub (pinned to 9690622007)

Solutions

  1. Rebuild the manifest to include every finding: scan.findingIds = [...new Set(findings.map(f => f.id))] Re-export the scan from the tool so manifest and findings are generated together If the extra finding is unintended, remove it from findings instead of adding it to the manifest
  2. Fix the exporting pipeline to append to the manifest when adding findings

Example fix

// before
parseSecurityScanBundle(raw); // finding orphaned from manifest
// after
raw.scan.findingIds = [...new Set(raw.findings.map(f => f.id))];
parseSecurityScanBundle(raw);
Defensive patterns

Strategy: validation

Validate before calling

const manifest = new Set(raw.scan.findingIds);
const orphaned = raw.findings.filter((f: { id: string }) => !manifest.has(f.id));
if (orphaned.length) throw new Error(`Findings missing from manifest: ${orphaned.map(f => f.id).join(", ")}`);

Try / catch

try {
	bundle = parseSecurityScanBundle(value);
} catch (err) {
	if (err instanceof Error && err.message.startsWith("Security scan omits finding from manifest:")) {
		raw.scan.findingIds = [...new Set(raw.findings.map(f => f.id))];
		bundle = parseSecurityScanBundle(raw);
	} else throw err;
}

Prevention

When it happens

Trigger: Parsing a bundle where a finding exists in bundle.findings but its id is missing from bundle.scan.findingIds.

Common situations: Appending new findings to an export without updating the manifest; a generator bug that snapshots the manifest before all findings are collected; hand-merging findings from another scan into the array.

Related errors


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