{"record":{"id":"b2e5c1b0c4d10a31","repo":"paperclipai/paperclip","slug":"input-reasoncode","errorCode":"input.reasonCode","errorMessage":"Gateway bearer token is expired or invalid","messagePattern":"Gateway bearer token is expired or invalid","errorType":"http","errorClass":"ToolGatewayHttpError","httpStatus":401,"severity":"error","filePath":"server/src/services/tool-gateway.ts","lineNumber":6671,"sourceCode":"\n  async function recordNamedGatewayAuthFailure(input: {\n    gatewayId?: string | null;\n    gatewayPublicId?: string | null;\n    bearerToken: string;\n    reasonCode: string;\n    clientMetadata: ReturnType<typeof safeClientMetadata>;\n  }): Promise<never> {\n    const token = input.bearerToken.trim();\n    const tokenId = namedGatewayTokenId(token);\n    const gatewayKey = input.gatewayId\n      ? `id:${input.gatewayId}`\n      : `public:${input.gatewayPublicId ?? \"unknown\"}`;\n    const tokenKey = tokenId\n      ? `id:${tokenId}`\n      : `hash:${hashGatewayToken(token).slice(0, 24)}`;\n    const gateway = await findGatewayForProtocolLocator(input);\n    if (!gateway) {\n      throw new ToolGatewayHttpError(\n        401,\n        \"Gateway bearer token is expired or invalid\",\n        input.reasonCode,\n      );\n    }\n    const gatewayState = await consumeProtocolRateLimit({\n      companyId: gateway.companyId,\n      counterKey: `mcp_gateway_auth_failure:gateway:${gatewayKey}`,\n      config: protocolLimits.authFailures,\n    });\n    const tokenState = await consumeProtocolRateLimit({\n      companyId: gateway.companyId,\n      counterKey: `mcp_gateway_auth_failure:token:${gatewayKey}:${tokenKey}`,\n      config: protocolLimits.authFailures,\n    });\n    const limited = gatewayState.limited || tokenState.limited;\n    if (limited) {\n      const limiterKeyClass = gatewayState.limited","sourceCodeStart":6653,"sourceCodeEnd":6689,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/tool-gateway.ts#L6653-L6689","documentation":"This 401 ToolGatewayHttpError is thrown inside recordNamedGatewayAuthFailure when the gateway bearer token presented to the named MCP gateway cannot be resolved to any existing gateway record (findGatewayForProtocolLocator returns null). The gateway routes MCP protocol requests by hashing the bearer token and looking up tool_mcp_gateway_tokens joined with tool_mcp_gateways; when no row matches, there is no gateway to authenticate against, so the request is rejected with the caller's reasonCode (typically 'gateway_token_invalid'). Repeated failures against the same gateway/token are additionally throttled with a 429 'gateway_auth_throttled'.","triggerScenarios":"Calling any MCP gateway protocol method (e.g. initialize, tools/call) with an Authorization: Bearer token whose hash does not match any row in tool_mcp_gateway_tokens — a token that was never issued for this instance, a token from a different environment (staging vs prod), or a token with a typo/whitespace-only difference that fails the hash lookup.","commonSituations":"Copying a pcgw_ token from a different Paperclip instance or company; an old token deleted from the board while a client (agent adapter, mcp client config) still caches it; rotating gateway tokens without updating the client's MCP server config; passing a regular agent API key instead of a gateway bearer token.","solutions":["Regenerate the gateway bearer token from the board UI (gateway settings) and update the client's Authorization header / MCP config with the new value.","Verify the token belongs to the same Paperclip instance and company as the gateway you are calling; cross-instance tokens can never match the hash lookup.","Check the token is the named gateway token (pcgw_ prefixed) and not an agent_api_key or other bearer credential.","If failures repeat, wait for the auth-failure throttle window to reset before retrying, or the request will be rejected with 429 gateway_auth_throttled."],"exampleFix":"// before: stale token cached in MCP client config\n\"headers\": { \"Authorization\": \"Bearer pcgw_old_deleted_token\" }\n// after: freshly minted gateway token from board gateway settings\n\"headers\": { \"Authorization\": \"Bearer pcgw_<newly-generated-token>\" }","handlingStrategy":"try-catch","validationCode":"// before calling the gateway, check the token shape and non-emptiness\nconst token = process.env.PAPERCLIP_GATEWAY_TOKEN?.trim();\nif (!token || !token.startsWith(\"pcgw_\")) {\n  throw new Error(\"PAPERCLIP_GATEWAY_TOKEN is missing or not a named gateway token\");\n}","typeGuard":"function isNamedGatewayToken(v: unknown): v is string {\n  return typeof v === \"string\" && v.trim().startsWith(\"pcgw_\") && v.trim().length > 10;\n}","tryCatchPattern":"try {\n  await mcpGatewayCall(token);\n} catch (err) {\n  if (err?.status === 401 && err?.code === \"gateway_token_invalid\") {\n    // token unknown to this instance: re-mint from board settings and reload config\n    token = await issueNewGatewayToken();\n  } else if (err?.status === 429 && err?.code === \"gateway_auth_throttled\") {\n    await sleep(err.details?.retryAfterMs ?? 60000);\n  } else throw err;\n}","preventionTips":["Store the gateway token in env/secret manager keyed per Paperclip instance to avoid cross-instance token reuse.","Rotate and update client configs in one step whenever tokens are reissued.","Never substitute an agent API key for a gateway bearer token.","Watch for repeated 401s and back off — repeated failures trigger the 429 auth-failure throttle."],"tags":["auth","http-401","bearer-token","mcp-gateway"],"backgroundTag":"authentication-required","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}