{"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":175,"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":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui/src/services/inference-service-providers.ts#L157-L193","documentation":"Thrown by InferenceServiceProvidersService.deleteRemote when DELETE /v1/providers/:id returns non-ok. Like removeRemote for characters, this does not parse JSON on success; it only checks ok. Message discards the status.","triggerScenarios":"client.api.v1.providers[':id'].$delete({ param: { id } }) resolves with ok=false. Typical: 401/403 (not authenticated / not owner), 404 (provider already deleted), 500 (server).","commonSituations":"Double delete; deleting a provider removed by another client; expired session; permission mismatch.","solutions":["Treat 404 as success if the provider is already gone locally (idempotent delete).","Re-authenticate on 401/403 before retrying.","Include res.status to allow 404-tolerant handling upstream."],"exampleFix":"// before\nif (!res.ok)\n  throw new Error('Failed to remove provider')\n\n// after\nif (!res.ok && res.status !== 404)\n  throw new Error(`Failed to remove provider ${providerId} (status ${res.status})`)","handlingStrategy":"try-catch","validationCode":"// Idempotent delete; only auth and a valid id are needed.\nfunction isValidProviderId(id: string): boolean {\n  return typeof id === 'string' && id.length > 0\n}","typeGuard":null,"tryCatchPattern":"try {\n  await deleteRemote(client, providerId)\n}\ncatch (error) {\n  // Patch service to expose status; tolerate 404 since the provider is already gone.\n  // On 401/403 re-authenticate and retry once.\n  throw error\n}","preventionTips":["Treat 404 on delete as success when the provider is already removed locally.","Embed res.status in the service error so callers can tolerate 404.","Disable the delete control after the first click to avoid double-delete races."],"tags":["network","http","providers","api","auth","not-found"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}