paperclipai/paperclip · error
${provider} returned an unreadable inventory response
Error message
${provider} returned an unreadable inventory response What it means
Thrown by jsonResponse, the shared inventory-response helper in chat-provider-inventory.ts, when response.json() rejects — i.e. the provider (GitHub or Slack) returned a body that is not parseable JSON. The helper is used by all chat-provider inventory listings, so this guards every provider call uniformly.
Source
Thrown at server/src/services/chat-provider-inventory.ts:49
const GITHUB_API_TIMEOUT_MS = 25_000;
function slackRequestSignal(): AbortSignal {
return AbortSignal.timeout(SLACK_API_TIMEOUT_MS);
}
function githubRequestSignal(): AbortSignal {
return AbortSignal.timeout(GITHUB_API_TIMEOUT_MS);
}
async function jsonResponse<T>(
response: Response,
provider: string,
): Promise<T> {
let body: unknown;
try {
body = await response.json();
} catch {
throw new Error(`${provider} returned an unreadable inventory response`);
}
if (!response.ok) {
const message =
body && typeof body === "object" && "message" in body
? String((body as { message?: unknown }).message)
: String(response.status);
throw new Error(`${provider} inventory failed: ${message}`);
}
return body as T;
}
/** List only Slack conversations where the installed bot is a member. */
export async function listSlackBotChannels(input: {
botToken: string;
fetch: typeof globalThis.fetch;
}): Promise<ChatProviderInventoryResult> {
const resources: ChatProviderResourceInventoryItem[] = [];
let cursor = "";View on GitHub (pinned to 01ad858492)
Solutions
- Retry the inventory request; transient truncation usually resolves.
- Verify the server can reach the provider API directly (curl the endpoint) without HTML-interposing proxies.
- Check the provider's base URL configuration for typos or outdated endpoints.
- Inspect provider status pages (Slack/GitHub) for ongoing incidents.
Defensive patterns
Strategy: retry
Validate before calling
// Sanity-check reachability/JSON before inventory:
const probe = await fetch(providerUrl, { method: 'HEAD' }).catch(() => null);
if (!probe || (probe.headers.get('content-type') ?? '').includes('text/html')) {
throw new Error('Provider endpoint unreachable or returning HTML; check proxy/base URL');
} Try / catch
try { const inv = await listProviderResources(input); }
catch (e) {
if (e.message.includes('unreadable inventory response')) {
await retryWithBackoff(() => listProviderResources(input), 3); // transient bad body
} else throw e;
} Prevention
- Verify provider base URLs and content types expected (application/json).
- Bypass HTML-injecting proxies/captive portals for provider API hosts.
- Retry transient body errors with backoff.
- Monitor provider status pages.
When it happens
Trigger: response.json() throws on: HTML error page from a proxy/captive portal, empty body, malformed JSON, or a stream read error even though the HTTP exchange completed.
Common situations: Corporate proxy or VPN intercepting provider API traffic; provider outage returning an error page with 200/5xx; network middleware truncating responses; calling through a URL that actually returns HTML (wrong base URL config).
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- Discord returned an unreadable response
- GitHub returned an unreadable webhook configuration. Reconne
- Plugin API routes accept JSON requests only
- Anthropic Managed Agents request failed with HTTP ${response
- Invalid worktree seed manifest at ${manifestPath}: ${error i
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/42f87b4042422ab2.
Report an issue: GitHub.