{"record":{"id":"2c591bfb64cf800b","repo":"paperclipai/paperclip","slug":"railway-workspace-discovery-failed","errorCode":"railway_workspace_discovery_failed","errorMessage":"Railway's hosted connection is connected, but workspace access could not be checked. Refresh actions to try again.","messagePattern":"Railway's hosted connection is connected, but workspace access could not be checked\\. Refresh actions to try again\\.","errorType":"error_code","errorClass":"RailwayError","httpStatus":null,"severity":"error","filePath":"server/src/services/railway.ts","lineNumber":179,"sourceCode":"  return Buffer.concat(chunks).toString(\"utf8\");\n}\n\n/** Discover a consented workspace without requesting account-wide API access. */\nexport async function discoverRailwayWorkspace(options: RailwayClientOptions): Promise<string> {\n  const send = (init: RequestInit) => options.request(RAILWAY_MCP_URL, { ...init, redirect: \"error\", signal: options.signal });\n  const headers = { Authorization: options.authorization };\n  const list = (requestHeaders: Record<string, string>) => send({\n    method: \"POST\", headers: mcpHttpRequestHeaders(requestHeaders),\n    body: JSON.stringify({ jsonrpc: \"2.0\", id: \"paperclip-railway-workspace-probe\", method: \"tools/call\", params: { name: \"list-workspaces\", arguments: {} } }),\n  });\n  let response = await list(headers);\n  if (response.status === 400) {\n    await response.body?.cancel();\n    response = await list(await initializeMcpHttpSession({ send, headers, requestId: \"paperclip-railway-workspace-probe\" }));\n  }\n  if (!response.ok) {\n    await response.body?.cancel();\n    throw new RailwayError(\"railway_workspace_discovery_failed\", \"Railway's hosted connection is connected, but workspace access could not be checked. Refresh actions to try again.\");\n  }\n  const body = await boundedResponseText(response, options.signal);\n  let data: Record<string, any>;\n  try {\n    const payload = record(parseMcpHttpResponseBody(body, response.headers.get(\"content-type\")));\n    const result = record(payload.result);\n    if (payload.error || result.isError) throw new Error(\"Workspace discovery failed\");\n    data = record(result.structuredContent ?? JSON.parse(result.content?.find((item: any) => item.type === \"text\")?.text ?? \"{}\"));\n  } catch { throw new RailwayError(\"railway_workspace_discovery_failed\", \"Railway could not list authorized workspaces. Refresh actions or reconnect and select a workspace.\"); }\n  const workspaceId = Array.isArray(data.workspaces) ? data.workspaces.find((workspace) => id.safeParse(workspace?.id).success)?.id : undefined;\n  if (!workspaceId) throw new RailwayError(\"railway_workspace_required\", \"No authorized Railway workspace was found. Reconnect Railway and select a workspace to enable direct operations.\", 403);\n  return workspaceId;\n}\n\nexport function createRailwayClient(options: RailwayClientOptions) {\n  if (!/^Bearer [^\\r\\n]+$/.test(options.authorization)) throw new RailwayError(\"railway_authorization_required\", \"Reconnect Railway to authorize API access.\", 401);\n  const secret = options.authorization.slice(7);\n  const redact = (value: unknown) => JSON.parse(redactSensitiveText(JSON.stringify(value).split(secret).join(\"[REDACTED]\")));","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/railway.ts#L161-L197","documentation":"Thrown by discoverRailwayWorkspace when the MCP-over-HTTP probe used to list the connected Railway account's workspaces returns a non-OK HTTP response (after a 400-triggered session re-initialization also fails). It signals that the hosted connection is authenticated enough to be 'connected', but the workspace discovery call itself failed, so the code cannot determine which workspace to operate on.","triggerScenarios":"The MCP HTTP endpoint returns 400 on the initial tools/call (session re-init attempted) and the retried list-workspaces request still returns a non-2xx status; network/proxy errors; Railway MCP service outage; expired hosted-connection token rejected at HTTP level.","commonSituations":"Railway-hosted OAuth token revoked or expired server-side; corporate proxy blocking the MCP endpoint; transient Railway incident; stale connection after Railway changed session handling.","solutions":["Click 'Refresh actions' (re-run the connection action probe) as the message suggests","Disconnect and reconnect the Railway hosted connection to obtain a fresh token/session","Verify network/proxy access to Railway's MCP endpoint from the server host","Check Railway status for an ongoing incident and retry later"],"exampleFix":"// before\nconst client = createRailwayClient({ authorization: staleAuthorization });\nawait client.workspaceId(); // throws railway_workspace_discovery_failed\n// after\nif (!(await railwayActionsHealthy(connection))) {\n  await reconnectRailway(connection); // refresh token/session before use\n}\nconst client = createRailwayClient({ authorization: freshAuthorization });","handlingStrategy":"retry","validationCode":"function canProbeRailway(conn) {\n  return typeof conn.authorization === \"string\" && conn.authorization.startsWith(\"Bearer \") && !conn.stale;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const ws = await client.workspaceId();\n} catch (e) {\n  if (e?.code === \"railway_workspace_discovery_failed\") {\n    await backoffRetry(() => refreshActionsAndRetry(conn), 3); // transient-safe retry\n  } else throw e;\n}","preventionTips":["Refresh the connection's actions/token before long-running operations","Monitor Railway status for MCP/API incidents","Avoid proxies that strip or rewrite the MCP HTTP endpoint","Surface a 'Refresh actions' affordance to users rather than failing silently"],"tags":["railway","network","workspace-discovery","http-error"],"backgroundTag":"http-error-response","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}