{"record":{"id":"8c74b8d52f5bbae8","repo":"musistudio/claude-code-router","slug":"plugin-route-pluginid-gateway-route-route-i","errorCode":null,"errorMessage":"[plugin:${route.pluginId}] Gateway route ${route.id} failed: ${formatError(error)}","messagePattern":"\\[plugin:(.+?)\\] Gateway route (.+?) failed: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/plugins/service.ts","lineNumber":452,"sourceCode":"      }\n      if (route.path && requestPath === route.path) {\n        return true;\n      }\n      if (route.pathPrefix && matchesPathPrefix(route.pathPrefix, requestPath)) {\n        return true;\n      }\n      return false;\n    });\n  }\n\n  async handleGatewayRoute(route: GatewayPluginRouteMatch, request: IncomingMessage, response: ServerResponse): Promise<void> {\n    if (!this.config) {\n      throw new Error(\"Gateway plugin service is not configured.\");\n    }\n    try {\n      await route.handler(request, response, this.createRouteContext(route.pluginId));\n    } catch (error) {\n      console.warn(`[plugin:${route.pluginId}] Gateway route ${route.id} failed: ${formatError(error)}`);\n      if (!response.headersSent) {\n        sendJson(response, 500, { error: { message: formatError(error) } });\n      } else {\n        response.destroy(error instanceof Error ? error : new Error(String(error)));\n      }\n    }\n  }\n\n  resolveProxyRoute(targetUrl: URL): GatewayPluginProxyRouteMatch | undefined {\n    let bestMatch: { matchedPathPrefix: string; route: RegisteredProxyRoute } | undefined;\n    for (const route of this.proxyRoutes) {\n      const matchedPathPrefix = matchProxyRoute(route, targetUrl);\n      if (matchedPathPrefix === undefined) {\n        continue;\n      }\n\n      if (!bestMatch || matchedPathPrefix.length > bestMatch.matchedPathPrefix.length) {\n        bestMatch = { matchedPathPrefix, route };","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/plugins/service.ts#L434-L470","documentation":"Warned by GatewayPluginService.handleGatewayRoute when a plugin-registered HTTP route handler throws. Unlike transforms, this is user-facing: if headers are not sent yet the client receives a 500 JSON error containing the formatted message; otherwise the response socket is destroyed with the error.","triggerScenarios":"A request matching a plugin's registered gateway route (handleRequest dispatch) whose handler throws — unhandled exception, missing route params, or failure of an internal call the handler makes.","commonSituations":"Plugin route handler bugs on edge-case inputs; handler depending on state (config, auth token) not initialized; client aborting mid-response triggering a write-after-abort throw.","solutions":["Find the failing handler via pluginId + route id in the log and fix the throw inside it.","Wrap the handler's fallible work and send a controlled error response instead of throwing.","Send the response before long async work to avoid destroy-on-late-throw paths.","Update/reinstall the plugin providing the route."],"exampleFix":"// before\nroute.handler = async (req, res) => { const id = await lookup(req.query.key); res.end(id.name); };\n// after\nroute.handler = async (req, res) => { const id = await lookup(String(req.query.key)).catch(() => null); if (!id) return sendJson(res, 404, {error:{message:'not found'}}); res.end(id.name); };","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Never throw from route handlers; send error responses instead","Send headers early to avoid destroy-on-late-throw","Handle client aborts explicitly"],"tags":["plugins","http-route","gateway","http-500"],"backgroundTag":"route-handler-threw","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}