can1357/oh-my-pi · error · Error

Invalid security store plan index at ${this.#indexPath()}

Error message

Invalid security store plan index at ${this.#indexPath()}

What it means

SecurityStore.#readIndex() validates the store's index.json on every read. If the optional planIds field is present but is not an array of strings, the store considers the index corrupt and throws this error naming the index path. The store refuses to continue with a partially malformed index rather than silently dropping plan references.

Source

Thrown at packages/coding-agent/src/security/store.ts:231

				planIds: [],
				updatedAt: new Date().toISOString(),
			});
		}
	}

	async #readIndex(): Promise<SecurityStoreIndex> {
		const value = (await readJsonFile(this.#indexPath())) as Partial<SecurityStoreIndex>;
		if (value.schemaVersion !== STORE_SCHEMA_VERSION || value.projectKey !== this.#projectKey) {
			throw new Error(`Unsupported security store index at ${this.#indexPath()}`);
		}
		if (!Array.isArray(value.scanIds) || !value.scanIds.every(id => typeof id === "string")) {
			throw new Error(`Invalid security store scan index at ${this.#indexPath()}`);
		}
		if (
			value.planIds !== undefined &&
			(!Array.isArray(value.planIds) || !value.planIds.every(id => typeof id === "string"))
		) {
			throw new Error(`Invalid security store plan index at ${this.#indexPath()}`);
		}
		return { ...value, planIds: value.planIds ?? [] } as SecurityStoreIndex;
	}

	async #writeIndex(index: SecurityStoreIndex): Promise<void> {
		await writeSecurityFileAtomic(this.#indexPath(), `${JSON.stringify(index, null, 2)}\n`);
	}

	async #putBundleUnlocked(input: SecurityScanBundle): Promise<void> {
		const bundle = parseSecurityScanBundle(input);
		if (bundle.scan.projectKey !== this.#projectKey) {
			throw new Error(`Security scan project key ${bundle.scan.projectKey} does not match ${this.#projectKey}`);
		}
		const scanDirectory = this.#scanDirectory(bundle.scan.id);
		await ensurePrivateDirectory(scanDirectory);
		await writeSecurityFileAtomic(
			path.join(scanDirectory, "findings.json"),
			`${JSON.stringify(bundle.findings, null, 2)}\n`,

View on GitHub (pinned to 9690622007)

Solutions

  1. Open the file named in the error and fix planIds to be an array of strings (or delete the key entirely — undefined planIds is accepted and defaults to []).
  2. If the index is corrupt beyond repair, back it up and delete index.json; the next SecurityStore.open() recreates it via #ensureIndex (note: this drops the scan/plan id registry, scans remain on disk but unreferenced).
  3. If the file was written by an older version, migrate it to schemaVersion 1 with valid scanIds/planIds arrays instead of hand-editing.
  4. Check for concurrent writers or interrupted atomic renames that may have left a truncated index.json.

Example fix

// before (index.json)
{ "schemaVersion": 1, "projectKey": "...", "repositoryRoot": "...", "scanIds": [], "planIds": "secplan_1", "updatedAt": "..." }
// after
{ "schemaVersion": 1, "projectKey": "...", "repositoryRoot": "...", "scanIds": [], "planIds": ["secplan_1"], "updatedAt": "..." }
Defensive patterns

Strategy: validation

Validate before calling

const idx = await Bun.file(path.join(store.projectDirectory, 'index.json')).json();
if (idx.planIds !== undefined && !(Array.isArray(idx.planIds) && idx.planIds.every(id => typeof id === 'string'))) {
  // repair or delete index.json before constructing the store
}

Type guard

function hasValidPlanIds(v: unknown): v is { planIds?: string[] } {
  const o = v as { planIds?: unknown };
  return o.planIds === undefined || (Array.isArray(o.planIds) && o.planIds.every(id => typeof id === 'string'));
}

Try / catch

try {
  const plans = await store.listPlans();
} catch (err) {
  if (err instanceof Error && err.message.includes('Invalid security store plan index')) {
    await fs.rm(path.join(store.projectDirectory, 'index.json')); // recreated on next open
  } else throw err;
}

Prevention

When it happens

Trigger: Calling any read-path API (index, storeDigest, listPlans, listScans, putBundle, putPlan, updateDisposition, updateValidation — anything reaching #ensureIndex/#readIndex) when the on-disk index.json has a planIds value that is not undefined and not an array of strings (e.g. planIds: "secplan_abc" or planIds: [1,2]).

Common situations: Manual hand-editing of index.json under the security state directory; an older store version writing a different planIds shape; a corrupted or partially written file restored from backup; a script rewriting the index and mistaking planIds for a string.

Related errors


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