paperclipai/paperclip · error · PaperclipApiError
${method} ${path} failed with ${status}
Error message
${method} ${path} failed with ${status} What it means
Same throw site as 450 (PaperclipApiError from requestJson on a non-OK response), but buildErrorMessage takes the shorter branch because the parsed body did not contain a string 'error' field — e.g. an empty body, plain text, or a JSON shape without 'error'. You still get status, method, and path on the error object, just no server message.
Source
Thrown at packages/mcp-server/src/client.ts:103
Authorization: `Bearer ${this.config.apiKey}`,
Accept: "application/json",
};
if (options.body !== undefined) {
headers["Content-Type"] = "application/json";
}
if ((options.includeRunId ?? isWriteMethod(method)) && this.config.runId) {
headers["X-Paperclip-Run-Id"] = this.config.runId;
}
const response = await fetch(url, {
method,
headers,
body: options.body === undefined ? undefined : JSON.stringify(options.body),
});
const parsedBody = await parseResponseBody(response);
if (!response.ok) {
throw new PaperclipApiError({
status: response.status,
method: method.toUpperCase(),
path,
body: parsedBody,
message: buildErrorMessage(method.toUpperCase(), path, response.status, parsedBody),
});
}
return parsedBody as T;
}
}
View on GitHub (pinned to 67001ec6eb)
Solutions
- Inspect the raw response — curl the same path directly to see the actual body.
- If a proxy is returning the error, fix the proxy/upstream config.
- On the client, read PaperclipApiError.body (parsedBody) for any non-'error' field that may carry detail.
Example fix
// status 502 with empty body -> 'GET /api/issues failed with 502' // diagnose directly curl -i -H "Authorization: Bearer $PAPERCLIP_API_KEY" $URL/api/issues
Defensive patterns
Strategy: try-catch
Type guard
function isPaperclipApiError(e: unknown): e is { status: number; method: string; path: string; body: unknown; message: string } {
return typeof e === 'object' && e !== null && 'status' in e && typeof (e as any).status === 'number';
} Try / catch
try { await client.requestJson(method, path, opts) }
catch (e) {
if (isPaperclipApiError(e) && e.body == null && e.status >= 500) { /* likely proxy/upstream — do not retry blindly */ }
throw e;
} Prevention
- When the message lacks ': <reason>', curl the path directly to see the real body.
- Suspect a reverse proxy or gateway when status is 502/503/504.
- Log PaperclipApiError.body to capture non-'error' envelope shapes.
When it happens
Trigger: Upstream returns non-2xx with an empty body (gateway/proxy 502/504), plain-text error from a reverse proxy, or a JSON envelope that uses a different key ('message', 'detail') than 'error'.
Common situations: A proxy in front of Paperclip returning HTML/text errors; the server crashed mid-request; a route that returns a non-standard error format.
Related errors
- ${method} ${path} failed with ${status}: ${body.error}
- API error ${response.status}: ${bytes.toString("utf8")}
- ${error instanceof Error ? error.message : String(error)}\n$
- companyId is required because PAPERCLIP_COMPANY_ID is not se
- agentId is required because PAPERCLIP_AGENT_ID is not set
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/0bc35d9c4af13199.
Report an issue: GitHub.