{"record":{"id":"cfbcd74e933de92c","repo":"paperclipai/paperclip","slug":"request-failed-with-status-response-status-cfbcd7","errorCode":null,"errorMessage":"Request failed with status ${response.status}","messagePattern":"Request failed with status (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/client/plugin.ts","lineNumber":974,"sourceCode":"  apiBase: string,\n  apiKey: string | undefined,\n  pluginId: string,\n  channel: string,\n  durationMs: number | undefined,\n): Promise<void> {\n  const controller = new AbortController();\n  const timer = durationMs === undefined ? null : setTimeout(() => controller.abort(), durationMs);\n  try {\n    const response = await fetch(buildApiUrl(\n      apiBase,\n      `/api/plugins/${encodeURIComponent(pluginId)}/bridge/stream/${encodeURIComponent(channel)}`,\n    ), {\n      headers: apiKey ? { authorization: `Bearer ${apiKey}` } : undefined,\n      signal: controller.signal,\n    });\n    if (!response.ok) {\n      const text = await response.text();\n      throw new Error(text.trim() || `Request failed with status ${response.status}`);\n    }\n    if (!response.body) return;\n    const reader = response.body.getReader();\n    const decoder = new TextDecoder();\n    while (true) {\n      const { value, done } = await reader.read();\n      if (done) break;\n      if (value) process.stdout.write(decoder.decode(value, { stream: true }));\n    }\n    const trailing = decoder.decode();\n    if (trailing) process.stdout.write(trailing);\n  } catch (error) {\n    if (error instanceof Error && error.name === \"AbortError\") return;\n    throw error;\n  } finally {\n    if (timer) clearTimeout(timer);\n  }\n}","sourceCodeStart":956,"sourceCodeEnd":992,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/client/plugin.ts#L956-L992","documentation":"streamPluginBridge throws when the GET /api/plugins/{pluginId}/bridge/stream/{channel} response is not ok. It uses the trimmed response body as the message, falling back to a status-code string when the body is empty.","triggerScenarios":"Streaming a plugin bridge channel that returns non-2xx: unknown pluginId, unknown channel, plugin not running, auth failure, or server error.","commonSituations":"Plugin not installed or not enabled for the company; typo in channel name; expired API key; plugin process offline.","solutions":["Verify pluginId via `paperclipai plugin list`.","Confirm the channel name with the plugin author/docs.","Check auth (re-run connect if the token expired).","Ensure the plugin runtime is healthy on the server side."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await streamPluginBridge(apiBase, apiKey, pluginId, channel, durationMs);\n} catch (err) {\n  const msg = err instanceof Error ? err.message : String(err);\n  if (/404/.test(msg)) console.error('Unknown plugin or channel; verify with `paperclipai plugin list`.');\n  if (/401|403/.test(msg)) await refreshToken();\n  throw err;\n}","preventionTips":["Verify pluginId and channel name before streaming.","Keep plugin runtimes healthy; handle reconnects on transient failures."],"tags":["cli","plugin","streaming","http"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}