can1357/oh-my-pi · error
ready requires log or port
Error message
ready requires log or port
What it means
Thrown by readySpec() when a daemon spec's ready section defines a timeout (required) but neither a log pattern nor a port. Readiness detection works by matching log output or probing a port; a ready clause with neither would never complete, so the parser rejects it up front.
Source
Thrown at packages/coding-agent/src/launch/protocol.ts:241
}
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");
const detached = source.detached === undefined ? false : booleanValue(source.detached, "spec.detached");
return {
name: stringValue(source.name, "spec.name"),
application: stringValue(source.application, "spec.application"),
args: stringArray(source.args, "spec.args"),
env: stringRecord(source.env, "spec.env"),
cwd: stringValue(source.cwd, "spec.cwd"),
pty: booleanValue(source.pty, "spec.pty"),
ready: source.ready === undefined ? undefined : readySpec(source.ready),
restart: restartPolicy(source.restart),
persist: booleanValue(source.persist, "spec.persist") || detached,
detached,View on GitHub (pinned to 9690622007)
Solutions
- Add either a log pattern (ready.log) or a port (ready.port) to the ready section
- Remove the ready section entirely if no readiness check is needed
- If the log pattern became empty, restore it or switch to a port probe
- Keep timeoutMs present and finite — it is mandatory in this section
Example fix
// before
parseDaemonSpec({ id: "d1", command: ["srv"], ready: { timeoutMs: 5000 } })
// after
parseDaemonSpec({ id: "d1", command: ["srv"], ready: { port: 8080, timeoutMs: 5000 } }) Defensive patterns
Strategy: validation
Validate before calling
interface ReadyInput { log?: string; port?: number; host?: string; timeoutMs: number }
function hasReadyTarget(r: ReadyInput): boolean {
return Boolean(r.log) || typeof r.port === "number";
}
if (!hasReadyTarget(ready)) throw new Error("ready requires log or port"); Type guard
function hasReadyTarget(r: { log?: string; port?: number }): boolean { return Boolean(r.log) || typeof r.port === "number"; } Try / catch
try {
const spec = parseDaemonSpec(raw);
} catch (err) {
if (err instanceof Error && err.message === "ready requires log or port") {
// drop the ready section or add a log/port condition before retrying
} else throw err;
} Prevention
- Always pair a ready.timeoutMs with either log or port in config templates
- Treat empty-string log patterns as absent — trim and omit them
- Add a config linter rule that flags ready sections without a target
- Prefer port probes when the service exposes one; they are the least ambiguous
When it happens
Trigger: parseDaemonSpec receiving ready: { timeoutMs: 5000 } with no log and no port; ready: { host: "localhost", timeoutMs: 5000 } (host alone doesn't count); log present but empty string "" combined with no port (empty string is falsy).
Common situations: Config where the log pattern was deleted or renamed during editing; setting only host/timeout and expecting defaults; empty-string log values that pass type checks but fail the presence check.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Unknown restart policy: ${policy}
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains an empty provider
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains a provider id wit
- ${name} path does not exist: ${trimmed}
- Anthropic thinking budget requires max_tokens greater than $
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e4e3730c542b57e4.
Report an issue: GitHub.