{"record":{"id":"dec8db727e7d6e2f","repo":"moeru-ai/airi","slug":"failed-to-remove-provider","errorCode":null,"errorMessage":"Failed to remove provider","messagePattern":"Failed to remove provider","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/services/inference-service-providers.ts","lineNumber":177,"sourceCode":"        validated: provider.status === 'configured',\n        validationBypassed: provider.status === 'bypassed',\n      },\n    }, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to add provider')\n\n    const item = await res.json()\n    options?.abortSignal?.throwIfAborted()\n    return normalize(item)\n  }\n\n  async function deleteRemote(client: InferenceServiceProvidersRemoteClient, providerId: string, options?: InferenceServiceProviderServiceOptions): Promise<void> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.providers[':id'].$delete({\n      param: { id: providerId },\n    }, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to remove provider')\n    options?.abortSignal?.throwIfAborted()\n  }\n\n  async function patchConfigRemote(\n    client: InferenceServiceProvidersRemoteClient,\n    providerId: string,\n    config: Record<string, unknown>,\n    status: ProviderValidationStatus,\n    options?: InferenceServiceProviderServiceOptions,\n  ): Promise<InferenceServiceProvider> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.providers[':id'].$patch({\n      param: { id: providerId },\n      json: {\n        config,\n        validated: status === 'configured',\n        validationBypassed: status === 'bypassed',\n      },","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/moeru-ai/airi/blob/0616eabd5ba89c7bcef0eb1da84694b9c285c573/packages/stage-ui/src/services/inference-service-providers.ts#L159-L195","documentation":"Thrown by deleteRemote when DELETE /api/v1/providers/:id returns non-2xx. Most commonly a 404 because the provider id no longer exists on the server (already deleted elsewhere or never synced), but auth failures and server errors collapse into the same message via the res.ok check.","triggerScenarios":"Deleting a provider that was already removed from another tab/device (404); deleting before the initial providers fetch completed so the id was never server-side; auth expired (401); server error during cascading cleanup of provider-owned resources (500).","commonSituations":"Multi-tab/multi-device provider management racing deletions; UI list built from stale state; backend restarted with a reset database while the client kept old ids.","solutions":["Check the HTTP status; treat 404 as success (already gone) in callers where idempotent deletion is acceptable.","Refresh the provider list (fetchRemote) and retry the delete against a confirmed-present id.","Re-authenticate if 401/403.","For 5xx, verify server logs for failing cascade deletes."],"exampleFix":"// before\nawait providersService.deleteRemote(client, providerId)\n\n// after - tolerate already-deleted providers\ntry {\n  await providersService.deleteRemote(client, providerId)\n}\ncatch (error) {\n  // re-check existence; if it is gone, the goal is already met\n  const providers = await providersService.fetchRemote(client)\n  if (providers[providerId])\n    throw error\n}","handlingStrategy":"try-catch","validationCode":"const providers = await providersService.fetchRemote(client, { abortSignal })\nif (!providers[providerId]) {\n  // already gone: nothing to delete\n  return\n}\nawait providersService.deleteRemote(client, providerId, { abortSignal })","typeGuard":null,"tryCatchPattern":"try {\n  await providersService.deleteRemote(client, providerId, { abortSignal })\n}\ncatch (error) {\n  const providers = await providersService.fetchRemote(client).catch(() => null)\n  if (providers && !providers[providerId])\n    return // 404-style: target already removed, treat as success\n  toast.error(errorMessageFrom(error) ?? 'Failed to remove provider')\n}","preventionTips":["Treat deletion as idempotent: reconcile with a fetch when it fails.","Refresh the provider list after any delete attempt, success or failure.","Disable delete actions for providers being edited elsewhere (multi-tab).","Pass an AbortSignal tied to the settings view lifetime."],"tags":["http","api-client","providers","remote","idempotency"],"backgroundTag":"http-error-status","analyzedSha":"0616eabd5ba89c7bcef0eb1da84694b9c285c573","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}