paperclipai/paperclip · error · ExeDevApiError
exe.dev API command failed (${response.status}) for: ${logCo
Error message
exe.dev API command failed (${response.status}) for: ${logCommand} What it means
Thrown as ExeDevApiError by runLifecycleCommand when the POST to config.apiUrl returns a non-2xx response. The error carries .status (the HTTP code) and .body (the raw response text) so callers can branch on the cause. The logCommand in the message is the redacted command string (redactCreateCommand strips --env, --prompt, --setup-script values) to keep secrets out of logs.
Source
Thrown at packages/plugins/sandbox-providers/exe-dev/src/plugin.ts:388
}
async function runLifecycleCommand(
config: ExeDevDriverConfig,
command: string,
logCommand = command,
): Promise<unknown> {
const response = await fetch(config.apiUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${resolveApiKey(config)}`,
"Content-Type": "text/plain; charset=utf-8",
},
body: command,
signal: AbortSignal.timeout(Math.min(config.timeoutMs, EXE_DEV_API_MAX_TIMEOUT_MS)),
});
const body = await response.text();
if (!response.ok) {
throw new ExeDevApiError(
`exe.dev API command failed (${response.status}) for: ${logCommand}`,
response.status,
body,
);
}
const trimmed = body.trim();
if (!trimmed) return null;
try {
return JSON.parse(trimmed);
} catch {
return body;
}
}
function parseVmRecord(value: unknown, depth = 0): ExeDevVmRecord | null {
if (depth > MAX_VM_RECORD_DEPTH) return null;
if (!value || typeof value !== "object" || Array.isArray(value)) return null;View on GitHub (pinned to 67001ec6eb)
Solutions
- Inspect error.status and error.body: 401/403 -> fix the API key (EXE_API_KEY or config.apiKey); 404 -> correct apiUrl; 400 -> correct command flags/image.
- For 5xx or transient network errors, retry with backoff (the provider already enforces AbortSignal.timeout capped by EXE_DEV_API_MAX_TIMEOUT_MS).
- Print the redacted logCommand from the error message to see which lifecycle op and flags failed without leaking secrets.
Example fix
// before
await runLifecycleCommand(config, cmd);
// after
try {
await runLifecycleCommand(config, cmd);
} catch (err) {
if (err instanceof ExeDevApiError) {
log.error({ status: err.status, body: err.body }, 'exe.dev lifecycle failed');
if (err.status === 401 || err.status === 403) throw new Error('exe.dev auth failed');
if (err.status >= 500) throw new RetryableError(err.message);
}
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
function isExeDevConfigValid(config: ExeDevDriverConfig): string | null {
if (!config.apiUrl || !/^https?:\/\//.test(config.apiUrl)) return 'apiUrl missing/invalid';
if (!(config.apiKey || process.env.EXE_API_KEY)) return 'apiKey missing';
return null;
} Type guard
function isExeDevApiError(x: unknown): x is { status: number; body: string; message: string } {
return x instanceof Error && (x as { name?: string }).name === 'ExeDevApiError';
} Try / catch
try {
await runLifecycleCommand(config, cmd);
} catch (err) {
if (err instanceof Error && (err as { name?: string }).name === 'ExeDevApiError') {
const e = err as { status: number; body: string };
if (e.status === 401 || e.status === 403) throw new Error('exe.dev auth failed');
if (e.status >= 500) throw new RetryableError(e.body || 'exe.dev 5xx');
throw new Error(`exe.dev ${e.status}: ${e.body}`);
}
throw err;
} Prevention
- Always branch on err.status; do not string-match the message.
- Log err.body for diagnosis but redact any echoed secrets.
- Cap retries on 5xx; treat 4xx as non-retryable except 429.
When it happens
Trigger: exe.dev API rejecting the lifecycle command: 401/403 from a bad or expired API key, 404 from a wrong apiUrl, 400 from a malformed command (bad image/name/flags), 5xx from an exe.dev outage, or a timeout surfaced as a non-OK response.
Common situations: API key revoked or rotated but config not updated; apiUrl pointing at the wrong endpoint; vm name over 63 chars from buildVmName colliding with an existing name; image/command flags not supported by the exe.dev CLI version; transient exe.dev 5xx during a deploy.
Related errors
- API error ${response.status}: ${bytes.toString("utf8")}
- Sandbox bridge mode requires a host-side Paperclip API token
- no Codex credentials provisioned for managed home "${input.e
- HTTP ${status}
- E2B sandbox environments require an API key in config or E2B
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/f432e6665b660e7f.
Report an issue: GitHub.