can1357/oh-my-pi · error

Unknown security operation: ${operationId}

Error message

Unknown security operation: ${operationId}

What it means

wait() blocks until the operation with the given id finishes, but only ids registered in this coordinator's in-memory #operations map (including those recovered from interrupted scans) are waitable. An unknown id means no such operation exists in this process, and unlike status(), wait() does not rescan the disk for terminal bundles before giving up.

Source

Thrown at packages/coding-agent/src/security/coordinator.ts:549

			.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
	}

	async cancel(operationId: string): Promise<boolean> {
		await this.#ensureRecovered();
		const record = this.#operations.get(operationId);
		if (!record) return false;
		if (["completed", "partial", "cancelled", "failed"].includes(record.snapshot.phase)) return false;
		if (record.snapshot.jobId && this.#host.asyncJobManager) {
			return this.#host.asyncJobManager.cancel(record.snapshot.jobId, { ownerId: this.#host.agentId });
		}
		record.abortController?.abort(new Error("Security scan cancelled"));
		return true;
	}

	async wait(operationId: string): Promise<SecurityOperationSnapshot> {
		await this.#ensureRecovered();
		const record = this.#operations.get(operationId);
		if (!record) throw new Error(`Unknown security operation: ${operationId}`);
		await record.promise;
		return { ...record.snapshot };
	}

	#update(record: SecurityOperationRecord, phase: SecurityOperationPhase, error?: string): void {
		record.snapshot.phase = phase;
		record.snapshot.updatedAt = toIsoTimestamp(this.#now);
		record.snapshot.error = error;
	}

	async #run(
		record: SecurityOperationRecord,
		plan: SecurityScanPlan,
		store: SecurityStore,
		signal: AbortSignal,
		reportProgress?: (text: string) => Promise<void>,
	): Promise<void> {
		const startedAt = toIsoTimestamp(this.#now);

View on GitHub (pinned to 9690622007)

Solutions

  1. Use the operationId exactly as returned by the start() snapshot — capture it at start time rather than reconstructing it.
  2. Check existence first with status(operationId), which also rescans interrupted/terminal operations on disk and returns null instead of throwing.
  3. If the id came from a prior process, use status() or listOperations() to find the recovered snapshot instead of wait().
  4. Fix id storage/passing in your automation (persist the id from start() and pass it through).

Example fix

// before
const snap = await coordinator.wait(opId); // throws if unknown
// after
const existing = await coordinator.status(opId);
const snap = existing ?? await coordinator.wait(opIdFromStart);
Defensive patterns

Strategy: try-catch

Validate before calling

const known = await coordinator.status(operationId);
if (!known) throw new Error(`operation ${operationId} unknown in this coordinator`);

Try / catch

try {
  const snap = await coordinator.wait(operationId);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Unknown security operation")) {
    const recovered = await coordinator.status(operationId); // rescans disk for terminal ops
    if (!recovered) throw err;
    return recovered;
  } else throw err;
}

Prevention

When it happens

Trigger: Calling wait(operationId) with an id that was never returned by start(), an id from a previous process that recovery didn't register, a typo'd/copied id, or waiting on an operation started in a different session's coordinator.

Common situations: Restarting the process and waiting on a pre-restart operationId whose scan was already terminal (recovery skips completed scans); automation holding stale ids; polling wait() before start() returned the snapshot.

Related errors


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