{"record":{"id":"5030278c25b96449","repo":"paperclipai/paperclip","slug":"oauth-reauthorization-required","errorCode":"oauth_reauthorization_required","errorMessage":"OAuth authorization has expired. Reconnect this app to continue.","messagePattern":"OAuth authorization has expired\\. Reconnect this app to continue\\.","errorType":"http","errorClass":"HttpError","httpStatus":422,"severity":"error","filePath":"server/src/services/tool-access.ts","lineNumber":4599,"sourceCode":"        appProfileOutcome = \"deleted\";\n      } else {\n        // `tool_mcp_gateways.profile_id` is ON DELETE RESTRICT, so a gateway\n        // pointing here keeps the row alive. Archive it instead — the policy\n        // engine only consults `active` profiles, and it has no entries left —\n        // and revoke the tokens those gateways already handed out, which are the\n        // one credential a caller could still present.\n        await db\n          .update(toolProfiles)\n          .set({ status: \"archived\", defaultAction: \"deny\", updatedAt: now })\n          .where(eq(toolProfiles.id, appProfile.id));\n        appProfileOutcome = \"archived\";\n        const revokedTokens = await db\n          .update(toolMcpGatewayTokens)\n          .set({ revokedAt: now, updatedAt: now })\n          .where(and(\n            eq(toolMcpGatewayTokens.companyId, connection.companyId),\n            inArray(toolMcpGatewayTokens.gatewayId, gatewayRows.map((row) => row.id)),\n            isNull(toolMcpGatewayTokens.revokedAt),\n          ))\n          .returning({ id: toolMcpGatewayTokens.id });\n        gatewayTokensRevoked = revokedTokens.length;\n        if (revokedTokens.length > 0) {\n          gatewaySessionsRevoked = (await db\n            .update(toolGatewaySessions)\n            .set({ revokedAt: now, updatedAt: now })\n            .where(and(\n              eq(toolGatewaySessions.companyId, connection.companyId),\n              inArray(toolGatewaySessions.gatewayTokenId, revokedTokens.map((row) => row.id)),\n              isNull(toolGatewaySessions.revokedAt),\n            ))\n            .returning({ id: toolGatewaySessions.id })).length;\n        }\n      }\n    }\n\n    // A local runtime already holds the injected credential inside a live child","sourceCodeStart":4581,"sourceCodeEnd":4617,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/tool-access.ts#L4581-L4617","documentation":"Refresh-grant failure mapping: a refresh_token grant returned invalid_grant, meaning the provider revoked or expired the authorization itself. Mapped to 422 oauth_reauthorization_required prompting the user to reconnect; the expired upstream authorization is at fault.","triggerScenarios":"Thrown at server/src/services/tool-access.ts:4441 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Reconnect the app to start a fresh OAuth authorization; the previous authorization grant expired."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}