{"record":{"id":"d8163e08bfcb7a12","repo":"different-ai/openwork","slug":"failed-to-remove-connector-instance-response-st","errorCode":null,"errorMessage":"Failed to remove connector instance (${response.status}).","messagePattern":"Failed to remove connector instance \\((.+?)\\)\\.","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/integration-data.tsx","lineNumber":1069,"sourceCode":"    },\n  });\n}\n\nexport function useRemoveConnectorInstance() {\n  const queryClient = useQueryClient();\n  const { runReauthableAction } = useOrgDashboard();\n\n  return useMutation({\n    mutationFn: async (connectorInstanceId: string) => {\n      await runReauthableAction(\"remove-connector-instance\", async () => {\n      const { response, payload } = await requestJson(\n        `/v1/connector-instances/${encodeURIComponent(connectorInstanceId)}/remove`,\n        { method: \"POST\" },\n        20000,\n      );\n\n      if (!response.ok) {\n        throw getRequestError(payload, response, `Failed to remove connector instance (${response.status}).`);\n      }\n\n      });\n      return connectorInstanceId;\n    },\n    onSuccess: () => {\n      queryClient.invalidateQueries({ queryKey: integrationQueryKeys.all });\n      queryClient.invalidateQueries({ queryKey: [\"plugins\"] });\n    },\n  });\n}\n","sourceCodeStart":1051,"sourceCodeEnd":1081,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/integration-data.tsx#L1051-L1081","documentation":"Thrown when removing a connector instance fails. The mutation POSTs to /v1/connector-instances/{id}/remove (20s timeout) and throws getRequestError(payload, response, ...) when response.ok is false. The returned error carries the server's `error` message or the fallback \"Failed to remove connector instance (<status>).\"; 403 {error:'reauth'} becomes ReauthRequiredError via getRequestError in den-flow.ts:527.","triggerScenarios":"POST remove returns non-ok: 401/403 (no permission or session expired), 404 (instance already removed), 409/422 (instance owns resources — plugins, policies, running imports — that block removal), 5xx (server-side cleanup of GitHub webhooks/installation failed).","commonSituations":"Double-clicking Remove so the second call 404s; org policy references the connector's plugins so removal is rejected; GitHub-side uninstall happened first causing server cleanup errors; expired session in a long-open dashboard tab.","solutions":["On 404, treat removal as successful: invalidate queries and drop the row locally.","If the server reports dependent resources, delete the linked plugins/policies first, then retry removal.","For 403 reauth errors, check isReauthRequiredError and start the sign-in flow.","On 5xx, retry once; verify in GitHub that the App installation/webhooks were cleaned up."],"exampleFix":"// before\nif (!response.ok) {\n  throw getRequestError(payload, response, `Failed to remove connector instance (${response.status}).`);\n}\n// after\nif (!response.ok) {\n  if (response.status === 404) return connectorInstanceId; // already removed\n  throw getRequestError(payload, response, `Failed to remove connector instance (${response.status}).`);\n}","handlingStrategy":"try-catch","validationCode":"// before remove\nif (!connectorInstanceId) throw new Error(\"connectorInstanceId is required.\");\nconst deps = getLinkedPlugins(connectorInstanceId);\nif (deps.length > 0) throw new Error(`Remove ${deps.length} linked plugins first.`);","typeGuard":"function isReauthError(e: unknown): e is ReauthRequiredError {\n  return e instanceof ReauthRequiredError;\n}","tryCatchPattern":"try {\n  await removeMutation.mutateAsync({ connectorInstanceId });\n} catch (error) {\n  if (isReauthError(error)) { startReauth(); return; }\n  if (/\\(404\\)/.test(error.message)) { queryClient.invalidateQueries(connectorKeys.list); return; }\n  showToast(error.message);\n}","preventionTips":["Treat 404 as success — removal is idempotent.","Show a confirmation dialog listing dependent plugins/policies before removing.","Keep removal button disabled while pending to prevent double-POST 404s.","After success, verify the GitHub App installation cleanup separately if needed."],"tags":["http","connectors","den-api","react-query"],"backgroundTag":"http-non-ok-response","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}