can1357/oh-my-pi · error · Error
Invalid findings list for ${scanId}
Error message
Invalid findings list for ${scanId} What it means
#getBundleUnlocked() reads scans/<scanId>/findings.json and requires it to be a JSON array. If the file exists but parses to anything else (object, string, null), the store declares the scan's findings corrupt and throws. This guards against truncated or tampered findings files being silently treated as zero findings.
Source
Thrown at packages/coding-agent/src/security/store.ts:323
if (plan) plans.push(plan);
}
return plans;
}
async getScan(scanId: string): Promise<SecurityScan | null> {
try {
return parseSecurityScan(await readJsonFile(path.join(this.#scanDirectory(scanId), "scan.json")));
} catch (error) {
if (isEnoent(error)) return null;
throw error;
}
}
async #getBundleUnlocked(scanId: string): Promise<SecurityScanBundle | null> {
const scan = await this.getScan(scanId);
if (!scan) return null;
const rawFindings = await readJsonFile(path.join(this.#scanDirectory(scanId), "findings.json"));
if (!Array.isArray(rawFindings)) throw new Error(`Invalid findings list for ${scanId}`);
const findings = rawFindings.map(parseSecurityFinding);
const report = await readOptionalText(path.join(this.#scanDirectory(scanId), "report.md"));
const sarifText = await readOptionalText(path.join(this.#scanDirectory(scanId), "results.sarif"));
const bundle: SecurityScanBundle = { scan, findings };
if (report !== undefined) bundle.report = report;
if (sarifText !== undefined) bundle.sarif = JSON.parse(sarifText) as Record<string, unknown>;
return parseSecurityScanBundle(bundle);
}
async getBundle(scanId: string): Promise<SecurityScanBundle | null> {
return withSecurityStoreWrite(this.#projectDirectory, () => this.#getBundleUnlocked(scanId));
}
async listScans(): Promise<SecurityScanSummary[]> {
const index = await this.#readIndex();
const summaries: SecurityScanSummary[] = [];
for (const scanId of [...index.scanIds].reverse()) {
const bundle = await this.getBundle(scanId);View on GitHub (pinned to 9690622007)
Solutions
- Inspect scans/<scanId>/findings.json, fix it to be a JSON array of findings (restore from the producing scanner's output if available).
- Re-run the scan and putBundle() a fresh, valid bundle to overwrite the corrupt findings file.
- If the scan directory is unrecoverable, delete it and remove its id from index.json scanIds so it stops appearing.
- Audit whatever wrote findings.json outside writeSecurityFileAtomic — all store writes must go through the atomic writer.
Example fix
// before (findings.json)
{ "findings": [...] }
// after
[ { "id": "...", ... }, ... ] // top-level array Defensive patterns
Strategy: validation
Validate before calling
const raw = await Bun.file(path.join(store.projectDirectory, 'scans', scanId, 'findings.json')).text();
if (!Array.isArray(JSON.parse(raw))) throw new Error(`corrupt findings for ${scanId}; re-scan required`); Type guard
function isFindingsArray(v: unknown): v is unknown[] {
return Array.isArray(v);
} Try / catch
try {
const bundle = await store.getBundle(scanId);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Invalid findings list')) {
// re-run the scan and putBundle() a fresh bundle
} else throw err;
} Prevention
- Never write findings.json with anything but the store's atomic writer (writeSecurityBundleToDirectory).
- Keep findings.json a top-level JSON array; don't wrap it in an object.
- Validate external/backup restores before dropping them into the store directory.
- Re-run scans rather than hand-repairing findings files.
When it happens
Trigger: Calling getBundle(), getFinding(), listScans(), updateDisposition(), updateValidation(), or compare() for a scan whose findings.json on disk parses to a non-array — e.g. the file was overwritten with "{}" by an external tool or truncated during a non-atomic write.
Common situations: Manual editing or scripting against the store directory; an interrupted write from a tool that bypassed writeSecurityFileAtomic; restoring files from a partial backup; a disk-full event during a raw (non-atomic) write; someone replacing findings.json with a JSON object.
Related errors
- Invalid security store plan index at ${this.#indexPath()}
- Auth broker response failed schema validation
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE must contain a JSON object
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} must
- Kilo device authorization response missing required fields
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/cf7bea07213fc3fc.
Report an issue: GitHub.