paperclipai/paperclip · error
Invalid CreateOS command timeout.
Error message
Invalid CreateOS command timeout.
What it means
track() validates the effective command timeout: it must be a positive integer within 1 ms..86,400,000 ms (24h). The value comes from the explicit timeoutOverride (e.g. params.timeoutMs from onEnvironmentExecute) or falls back to the config's timeoutMs. This error is thrown when the resolved value is not an integer, is less than 1, or exceeds 24 hours.
Solutions
- Pass an integer millisecond value between 1 and 86,400,000 as the operation's timeoutMs (or timeoutOverride).
- Check the CreateOS environment config: timeoutMs must be a valid integer ms value — run onEnvironmentValidateConfig to catch it early.
- Convert second-based durations to milliseconds (seconds * 1000) before passing them.
- Guard computed timeouts: reject NaN/undefined with a default instead of forwarding them to the driver.
Example fix
// before: seconds value forwarded as if milliseconds
await driver.execute({ ...params, timeoutMs: 300 });
// after: normalize and validate before calling
const timeoutMs = Math.floor(Number(process.env.CMD_TIMEOUT_S ?? 600) * 1000);
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86_400_000) {
throw new RangeError(`timeoutMs must be an integer in [1, 86400000], got ${timeoutMs}`);
}
await driver.execute({ ...params, timeoutMs }); Defensive patterns
Strategy: validation
Validate before calling
function assertValidTimeoutMs(ms) {
const n = Number(ms);
if (!Number.isInteger(n) || n < 1 || n > 86_400_000) {
throw new RangeError(`timeoutMs must be an integer in [1, 86400000], got ${ms}`);
}
return n;
}
const timeoutMs = assertValidTimeoutMs(params.timeoutMs ?? config.timeoutMs); Type guard
function isValidTimeoutMs(v: unknown): v is number {
return typeof v === "number" && Number.isInteger(v) && v >= 1 && v <= 86_400_000;
} Try / catch
try {
return await driver.execute({ ...params, timeoutMs });
} catch (e) {
if (e.message === "Invalid CreateOS command timeout.") {
logger.warn(`bad timeoutMs=${params.timeoutMs}; falling back to 600000ms`);
return await driver.execute({ ...params, timeoutMs: 600_000 });
}
throw e;
} Prevention
- Always express timeouts in integer milliseconds; convert from seconds explicitly.
- Run onEnvironmentValidateConfig once at setup to catch bad timeoutMs in config.
- Clamp computed timeouts to [1, 86_400_000] before invoking the driver.
- Never use 0 or undefined to mean 'no timeout' — pick a large-but-valid cap like 86_400_000.
When it happens
Trigger: onEnvironmentExecute is called with a non-integer timeoutMs (NaN, undefined when no config default exists, a float), a zero or negative timeout, or a timeout above 86,400,000; or the CreateOS config's timeoutMs itself is out of range and no override is supplied.
Common situations: Passing a duration in seconds (e.g. 300) instead of milliseconds; computing timeout from a Date diff that yields NaN; serializing timeouts through JSON where they become strings; configuring timeoutMs: 0 intending 'no timeout' when 0 is invalid; typo'd config like timeout: 60000 that leaves timeoutMs undefined.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- --agent-name cannot be empty.
- invalid_response
- Cloud readiness requires positive finite timeout and poll…
- Codex working directory is required
- GitHub webhook configuration is incomplete
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/5e8857de8d0cbe00.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/plugin.ts:85
}
async function stopActive(scope: string) {
const calls = [...(active.get(scope) ?? [])];
for (const call of calls) call.controller.abort();
await Promise.all(calls.map((call) => call.done));
}
async function track<T>(
params: PluginEnvironmentDriverBaseParams & { lease: PluginEnvironmentLease },
work: (client: CreateosClient, signal: AbortSignal) => Promise<T>,
timeoutOverride?: number,
): Promise<T> {
if (!params.lease.providerLeaseId || !metadataMatches(params, params.lease.metadata)) throw new Error("CreateOS execution requires a lease from this environment.");
const scope = key(params, params.lease.providerLeaseId);
if (shuttingDown || closing.has(scope) || unconfirmedCleanup.has(scope)) throw new Error("CreateOS lease is closing or requires cleanup.");
const config = parseConfig(params.config);
const timeoutMs = timeoutOverride ?? config.timeoutMs;
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86_400_000) throw new Error("Invalid CreateOS command timeout.");
const controller = new AbortController();
let finish!: () => void;
const entry: Active = { controller, done: new Promise<void>((resolve) => { finish = resolve; }) };
const calls = active.get(scope) ?? new Set<Active>();
calls.add(entry);
active.set(scope, calls);
try {
return await work(new CreateosClient(config), AbortSignal.any([controller.signal, AbortSignal.timeout(timeoutMs)]));
} catch (error) {
if (error instanceof CreateosCleanupError) unconfirmedCleanup.add(scope);
throw error;
} finally {
calls.delete(entry);
if (calls.size === 0) active.delete(scope);
finish();
}
}
View on GitHub (pinned to 3f1d897a7c)