paperclipai/paperclip · error · ApiRequestError
Request failed with status ${response.status}
Error message
Request failed with status ${response.status} What it means
Fallback thrown by parseFetchResponse() in the asset command when response.ok is false AND the parsed body has no string `error` field. Unlike [0] (a plain Error), this is wrapped into an ApiRequestError(response.status, message, ...) so callers can branch on .status. Used by asset upload/download paths that go through parseFetchResponse rather than the PaperclipApiClient.
Source
Thrown at cli/src/commands/client/asset.ts:130
async function downloadAsset(apiBase: string, apiKey: string | undefined, assetId: string): Promise<Buffer> {
const response = await fetch(buildApiUrl(apiBase, apiPath`/api/assets/${assetId}/content`), {
headers: apiKey ? { authorization: `Bearer ${apiKey}` } : undefined,
});
if (!response.ok) {
await parseFetchResponse(response);
}
return Buffer.from(await response.arrayBuffer());
}
async function parseFetchResponse(response: Response): Promise<unknown> {
const text = await response.text();
const parsed = text.trim() ? safeJson(text) : null;
if (!response.ok) {
const message =
typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string"
? parsed.error
: `Request failed with status ${response.status}`;
throw new ApiRequestError(response.status, message, undefined, parsed);
}
return parsed;
}
function buildApiUrl(apiBase: string, path: string): string {
const url = new URL(apiBase);
url.pathname = `${url.pathname.replace(/\/+$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
return url.toString();
}
function safeJson(text: string): unknown {
try {
return JSON.parse(text);
} catch {
return text;
}
}
View on GitHub (pinned to 67001ec6eb)
Solutions
- If 401/403: supply --api-key or run `paperclipai login`.
- If 413: shrink the file or raise the server/proxy body-size cap.
- If 404 on download: verify the asset id and company scope.
- If the status is 5xx: retry once the server is healthy; check server logs.
- If the body is HTML (proxy page), bypass the proxy or fix its error response format.
Defensive patterns
Strategy: try-catch
Validate before calling
import fs from 'node:fs';
function preflightAsset(file: string, maxBytes = 50 * 1024 * 1024) {
const st = fs.statSync(file);
if (!st.isFile()) throw new Error(`${file} is not a regular file`);
if (st.size > maxBytes) throw new Error(`${file} is ${st.size} bytes (> ${maxBytes}); will likely 413`);
} Type guard
function isApiErrorBody(v: unknown): v is { error: string } {
return typeof v === 'object' && v !== null && typeof (v as any).error === 'string';
} Try / catch
try { await uploadAsset(...); }
catch (err) {
if (err instanceof ApiRequestError) {
if (err.status === 401 || err.status === 403) console.error('Auth failed; run paperclipai login or pass --api-key.');
else if (err.status === 413) console.error('Asset too large; raise the server/proxy body limit or shrink the file.');
else console.error(`Asset HTTP ${err.status}: ${err.message}`);
process.exit(2);
}
throw err;
} Prevention
- Pre-flight check file size against the server's body limit.
- Ensure authentication (--api-key or board login) before upload.
- Use a supported MIME (see cli inferContentTypeFromPath and server allowlist).
- Avoid proxies that rewrite errors to HTML; the fallback message is least informative in that case.
When it happens
Trigger: Asset upload returns 4xx/5xx (e.g. 413 payload too large, 415 unsupported media type, 401 unauthenticated, 403 forbidden, 404 unknown asset on download, 500) and the body is empty or lacks `{ error: string }`. Also if the server's asset route is fronted by a proxy that emits plain status pages.
Common situations: Uploading a file larger than the server/proxy limit. Wrong/missing API key (401). Asset namespace/alt strings rejected by validation. Downloading an asset id that does not exist. Reverse proxy returning 413/502 with an HTML body.
Related errors
- Request failed: ${response.status}
- --file is required
- Export request returned no data
- Failed to fetch from remote "${remote}": ${extractExecSyncEr
- CLI auth challenge was cancelled.
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/c0c381d19b02ecc8.
Report an issue: GitHub.