can1357/oh-my-pi · error

Unknown readiness condition: ${String(item)}

Error message

Unknown readiness condition: ${String(item)}

What it means

Thrown by readyPendingList() when an element of the readyPending array is neither 'log' nor 'port'. Each entry must be a recognized readiness condition type; anything else is rejected.

Source

Thrown at packages/coding-agent/src/launch/protocol.ts:229

function restartPolicy(value: unknown): DaemonRestartPolicy {
	const policy = stringValue(value, "restart policy");
	if (policy === "no" || policy === "on-failure" || policy === "always") return policy;
	throw new Error(`Unknown restart policy: ${policy}`);
}

function daemonSignal(value: unknown): DaemonSignal {
	const signal = stringValue(value, "signal");
	if (signal === "SIGINT" || signal === "SIGTERM" || signal === "SIGHUP") return signal;
	if (signal === "SIGQUIT" || signal === "SIGKILL") return signal;
	throw new Error(`Unknown daemon signal: ${signal}`);
}

function readyPendingList(value: unknown): ("log" | "port")[] {
	if (!Array.isArray(value)) throw new Error("daemon.readyPending must be an array");
	const result: ("log" | "port")[] = [];
	for (const item of value) {
		if (item !== "log" && item !== "port") throw new Error(`Unknown readiness condition: ${String(item)}`);
		result.push(item);
	}
	return result;
}

function readySpec(value: unknown): DaemonReadySpec {
	const source = record(value, "ready");
	const log = optionalString(source.log, "ready.log");
	const port = optionalNumber(source.port, "ready.port");
	const host = optionalString(source.host, "ready.host");
	const timeoutMs = numberValue(source.timeoutMs, "ready.timeoutMs");
	if (!log && port === undefined) throw new Error("ready requires log or port");
	return { log, port, host, timeoutMs };
}

/** Decode and validate a daemon launch specification. */
export function parseDaemonSpec(value: unknown): DaemonSpec {
	const source = record(value, "daemon spec");

View on GitHub (pinned to 9690622007)

Solutions

  1. Restrict entries to exactly "log" or "port"
  2. Upgrade client and daemon together if a new readiness kind was introduced
  3. Normalize case and remove unknown entries at the producer before sending

Example fix

// before
snapshot = { id: "d1", state: "starting", readyPending: ["http"] }
// after
snapshot = { id: "d1", state: "starting", readyPending: ["port"] }
Defensive patterns

Strategy: validation

Validate before calling

function isReadyPending(v: unknown): v is ("log" | "port")[] {
  return Array.isArray(v) && v.every((x) => x === "log" || x === "port");
}
// filter unknown entries defensively before parsing:
snapshot.readyPending = (snapshot.readyPending ?? []).filter((x) => x === "log" || x === "port");

Type guard

function isReadinessCondition(v: unknown): v is "log"|"port" { return v === "log" || v === "port"; }

Try / catch

try {
  const snap = parseDaemonSnapshot(raw);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Unknown readiness condition")) {
    // newer daemon introduced a condition type: upgrade client or drop unknown entries
  } else throw err;
}

Prevention

When it happens

Trigger: parseDaemonSnapshot receiving readyPending entries like "http", "tcp", "Log" (capitalized), or non-string values (numbers, objects) inside the array.

Common situations: Newer daemon adding a readiness kind the client doesn't know; custom conditions invented by wrapper tooling; case inconsistencies from code that uppercases enum strings.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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