paperclipai/paperclip · error

${provider} inventory failed: ${message}

Error message

${provider} inventory failed: ${message}

What it means

Thrown by jsonResponse when the provider responds with a non-OK HTTP status (response.ok is false). The helper extracts the provider's 'message' field from the JSON body when present, otherwise falls back to the numeric status, and wraps it as '<provider> inventory failed: <message>'. This is the generic upstream-API-error path for GitHub/Slack inventory calls.

Source

Thrown at server/src/services/chat-provider-inventory.ts:56

  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 = "";
  do {
    const url = new URL("https://slack.com/api/conversations.list");
    url.searchParams.set("types", "public_channel,private_channel");
    url.searchParams.set("exclude_archived", "true");
    url.searchParams.set("limit", "200");
    if (cursor) url.searchParams.set("cursor", cursor);
    const response = await input.fetch(url, {

View on GitHub (pinned to 01ad858492)

Solutions

  1. Read the embedded message: fix the specific cause (refresh token, add scopes, back off).
  2. If 401/403, reconnect the chat connection to obtain fresh tokens/scopes.
  3. If 429, implement/respect exponential backoff using Retry-After headers.
  4. Check provider status pages and retry on 5xx.
  5. Verify the account/app has not been deactivated or suspended.
Defensive patterns

Strategy: try-catch

Try / catch

try { const inv = await listProviderResources(input); }
catch (e) {
  const msg = e.message; // '<Provider> inventory failed: <detail>'
  if (msg.includes('429') || /rate/i.test(msg)) await backoffAndRetry();
  else if (/401|403|token|scope/i.test(msg)) await promptReconnect();
  else throw e;
}

Prevention

When it happens

Trigger: Any 4xx/5xx from the provider during inventory: 401 invalid/expired token, 403 missing scopes or suspended app, 404 wrong resource/endpoint, 429 rate limited, 5xx provider outage.

Common situations: Expired or revoked Slack bot token / GitHub installation token; insufficient scopes after reinstall; hitting Slack/GitHub rate limits during large inventory scans; provider incident.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/cb06569c5c2c0e56. Report an issue: GitHub.