can1357/oh-my-pi · error

Security scan contains duplicate finding ids

Error message

Security scan contains duplicate finding ids

What it means

parseSecurityScanBundle validates the structural contract of a scan bundle beyond the schema: finding ids must be unique. Duplicate ids make by-id lookups and diffing ambiguous, so parsing fails fast with this error. It runs for every bundle read from disk, imported from Codex/SARIF, or cached in the store.

Source

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

	const result = securityScanSchema(value);
	if (result instanceof type.errors) throw schemaError("Security scan", result);
	return result as SecurityScan;
}

export function parseSecurityScanPlan(value: unknown): SecurityScanPlan {
	const { securityScanPlanSchema } = getSecurityContractSchemas();
	const result = securityScanPlanSchema(value);
	if (result instanceof type.errors) throw schemaError("Security scan plan", result);
	return result as SecurityScanPlan;
}

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`);

View on GitHub (pinned to 9690622007)

Solutions

  1. Regenerate the bundle from the scanner instead of hand-merging exports
  2. If merging intentionally, re-key ids to be unique (e.g. prefix with source scan id) and keep scan.findingIds consistent
  3. Validate before parse: JSON.parse the file and check new Set(ids).size === ids.length
  4. Re-export a single authoritative scan rather than concatenating multiple runs

Example fix

// before
const merged = { ...a, findings: [...a.findings, ...b.findings] };
parseSecurityScanBundle(merged); // throws: duplicate ids
// after
const findings = [...a.findings, ...b.findings.map(f => ({ ...f, id: `${b.scan.id}:${f.id}` }))];
parseSecurityScanBundle({ ...a, findings, scan: { ...a.scan, findingIds: findings.map(f => f.id) } });
Defensive patterns

Strategy: validation

Validate before calling

const raw = JSON.parse(text);
const ids = raw.findings.map((f: { id: string }) => f.id);
if (new Set(ids).size !== ids.length) throw new Error("Bundle has duplicate finding ids; regenerate it");

Try / catch

try {
	bundle = parseSecurityScanBundle(value);
} catch (err) {
	if (err instanceof Error && err.message === "Security scan contains duplicate finding ids") {
		// dedupe/re-key ids or re-export the scan
	} else throw err;
}

Prevention

When it happens

Trigger: Loading a JSON bundle file whose findings array contains two entries with the same id; importing a SARIF or Codex bundle whose conversion produced colliding ids.

Common situations: Manually merging two scan exports by concatenating findings arrays; a generator bug reusing placeholder ids; SARIF results from multiple runs merged without re-keying ids.

Related errors


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