{"record":{"id":"c21003080eb100c2","repo":"paperclipai/paperclip","slug":"railway-action-blocked","errorCode":"railway_action_blocked","errorMessage":"This Railway action cannot bind its effects to an approved target. Use redeploy, restart, or rollback for an existing deployment.","messagePattern":"This Railway action cannot bind its effects to an approved target\\. Use redeploy, restart, or rollback for an existing deployment\\.","errorType":"error_code","errorClass":"RailwayError","httpStatus":403,"severity":"error","filePath":"server/src/services/railway.ts","lineNumber":252,"sourceCode":"      throw new RailwayError(\"railway_target_mismatch\", \"The service and environment do not belong to the selected Railway project.\", 403);\n    }\n    return data.serviceInstance;\n  }\n\n  async function validateDeployment(args: Record<string, any>) {\n    const data = await query(RAILWAY_QUERIES.deployment, { deploymentId: args.deploymentId });\n    const d = record(data.deployment);\n    if (d.id !== args.deploymentId || d.projectId !== args.projectId || d.environmentId !== args.environmentId || d.serviceId !== args.serviceId) throw new RailwayError(\"railway_target_mismatch\", \"The deployment does not belong to the selected Railway target.\", 403);\n    return d;\n  }\n\n  return {\n    async probe(workspaceId: string) {\n      if (!id.safeParse(workspaceId).success) throw new RailwayError(\"railway_workspace_required\", \"Choose an authorized Railway workspace before checking API access.\", 400);\n      await query(RAILWAY_QUERIES.projects, { workspaceId, first: 1 });\n    },\n    async call(name: string, parameters: unknown): Promise<unknown> {\n      if (isRailwayToolBlocked(name)) throw new RailwayError(\"railway_action_blocked\", \"This Railway action cannot bind its effects to an approved target. Use redeploy, restart, or rollback for an existing deployment.\", 403);\n      const operation = name.slice(RAILWAY_TOOL_PREFIX.length) as Operation;\n      if (!name.startsWith(RAILWAY_TOOL_PREFIX) || !Object.hasOwn(schema, operation)) throw new RailwayError(\"railway_unknown_tool\", \"Unknown Railway operation.\", 400);\n      const parsed = schema[operation].safeParse(parameters);\n      if (!parsed.success) throw new RailwayError(\"railway_invalid_arguments\", \"Invalid Railway operation arguments. Use the exact IDs and limits in the action schema.\", 400);\n      const args = parsed.data as Record<string, any>;\n      let result: unknown;\n      if (operation === \"list-projects\") result = await query(RAILWAY_QUERIES.projects, args);\n      else if (operation === \"list-services\" || operation === \"list-environments\") result = await query(operation === \"list-services\" ? RAILWAY_QUERIES.services : RAILWAY_QUERIES.environments, args);\n      else {\n        const instance = await validateTarget(args);\n        const deployment = args.deploymentId ? await validateDeployment(args) : null;\n        switch (operation) {\n          case \"service-status\": result = instance; break;\n          case \"deployment-status\": result = deployment; break;\n          case \"list-deployments\": result = await query(RAILWAY_QUERIES.deployments, { input: { projectId: args.projectId, environmentId: args.environmentId, serviceId: args.serviceId }, first: args.first, after: args.after }); break;\n          case \"read-logs\": {\n            if (args.startDate && args.endDate && Date.parse(args.startDate) > Date.parse(args.endDate)) throw new RailwayError(\"railway_invalid_arguments\", \"Log start time must precede end time.\", 400);\n            const data = await query(args.kind === \"build\" ? RAILWAY_QUERIES.buildLogs : RAILWAY_QUERIES.runtimeLogs, { deploymentId: args.deploymentId, limit: args.limit, startDate: args.startDate, endDate: args.endDate, filter: args.filter });","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/railway.ts#L234-L270","documentation":"`call()` refuses tools listed in RAILWAY_BLOCKED_TOOLS (e.g. deploy-revision, railway-agent, accept-deploy) before any schema lookup, throwing `railway_action_blocked` with HTTP 403. These Railway actions cannot be bound to a pre-approved repository+revision target, so Paperclip blocks them and directs callers to redeploy/restart/rollback on an existing deployment instead.","triggerScenarios":"Invoking `paperclip-railway-deploy-revision`, `accept-deploy`, or `railway-agent` (any casing/separator form, since names are normalized before the blocklist check), or an old catalog entry that maps to a blocked tool.","commonSituations":"An agent picking deploy-revision from stale tool documentation; a client built against an older catalog that still exposes deploy-revision; attempting a fresh deploy through the direct bridge instead of using an existing deployment.","solutions":["Switch to paperclip-railway-redeploy on an existing eligible deployment (check canRedeploy first) instead of deploy-revision","Use restart for a no-rebuild recovery, or rollback to a previous known-good deployment","Update the agent's tool catalog to the current RAILWAY_TOOLS list which no longer advertises blocked tools"],"exampleFix":"// before\nawait client.call(\"paperclip-railway-deploy-revision\", args);\n// after\nconst d = await client.call(\"paperclip-railway-deployment-status\", { projectId: p, environmentId: e, serviceId: s, deploymentId: id });\nif (d.canRedeploy) await client.call(\"paperclip-railway-redeploy\", { projectId: p, environmentId: e, serviceId: s, deploymentId: id });","handlingStrategy":"validation","validationCode":"import { isRailwayToolBlocked } from \"../services/railway.js\";\nif (isRailwayToolBlocked(toolName)) throw new Error(`use redeploy/restart/rollback instead of ${toolName}`);","typeGuard":null,"tryCatchPattern":"try {\n  return await client.call(name, args);\n} catch (e) {\n  if (isRailwayError(e) && e.code === \"railway_action_blocked\") {\n    return client.call(\"paperclip-railway-redeploy\", { projectId: args.projectId, environmentId: args.environmentId, serviceId: args.serviceId, deploymentId: args.deploymentId ?? currentDeploymentId });\n  }\n  throw e;\n}","preventionTips":["Enumerate tools only from the exported RAILWAY_TOOLS list, which already excludes blocked tools","Never hand-write tool names from older docs or hosted-MCP catalogs","Prefer redeploy on an existing deployment for fresh builds of approved code","Refresh the tool catalog after upgrading Paperclip"],"tags":["railway","blocked-action","policy","governance"],"backgroundTag":"operation-not-supported","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"}