{"record":{"id":"7b190271f24fde34","repo":"sipeed/picoclaw","slug":"failed-to-delete-credential-v","errorCode":null,"errorMessage":"failed to delete credential: %v","messagePattern":"failed to delete credential: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"web/backend/api/oauth.go","lineNumber":478,"sourceCode":"\t}\n\tdefer r.Body.Close()\n\n\tvar req struct {\n\t\tProvider string `json:\"provider\"`\n\t}\n\tif err = json.Unmarshal(body, &req); err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"invalid JSON: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tprovider, err := normalizeOAuthProvider(req.Provider)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif err := oauthDeleteCredential(provider); err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"failed to delete credential: %v\", err), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif err := h.syncProviderAuthMethod(provider, \"\"); err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"failed to update config: %v\", err), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t_ = json.NewEncoder(w).Encode(map[string]any{\n\t\t\"status\":   \"ok\",\n\t\t\"provider\": provider,\n\t})\n}\n\nfunc renderOAuthCallbackPage(w http.ResponseWriter, flowID, status, title, errMsg string) {\n\tpayload := map[string]string{\n\t\t\"type\":   \"picoclaw-oauth-result\",\n\t\t\"flowId\": flowID,","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/web/backend/api/oauth.go#L460-L496","documentation":"Returned as HTTP 500 by POST /api/oauth/logout when oauthDeleteCredential fails. That function loads the on-disk credential store, deletes the provider's entry, and saves the store; failure means LoadStore or SaveStore errored — typically a corrupt/unreadable credentials file or a filesystem problem (permissions, read-only mount, disk full). Note the implication: if the store file itself is broken, you cannot log out through the API because logout must rewrite that same file.","triggerScenarios":"POST /api/oauth/logout {\"provider\":\"openai\"} when the credential store JSON is invalid (LoadStore unmarshal error), the file or its directory is not writable by the backend user, or the disk is full (SaveStore).","commonSituations":"Hand-edited or partially written credentials file; running the backend under a different UID than the file owner; credentials file on a read-only mount in a container; ENOSPC.","solutions":["Read the wrapped %v — a JSON syntax error means the store file is corrupt; rename it aside and retry (logout then trivially succeeds with an empty store).","Fix permissions/ownership on the credentials file and its parent directory for the backend process user.","Free disk space if the error is a write failure.","Restart the backend after replacing the store file so any in-process state is consistent."],"exampleFix":"# before\n$ curl -X POST localhost:8080/api/oauth/logout -d '{\"provider\":\"openai\"}'\n{\"message\":\"failed to delete credential: parsing auth store: invalid character ...\"}\n\n# after\n$ mv ~/.config/picoclaw/auth.json ~/.config/picoclaw/auth.json.bak\n$ curl -X POST localhost:8080/api/oauth/logout -H 'Content-Type: application/json' -d '{\"provider\":\"openai\"}'\n{\"status\":\"ok\",\"provider\":\"openai\"}","handlingStrategy":"try-catch","validationCode":"await fs.access(credsFile, fs.constants.W_OK); // store must be rewritable for logout to succeed","typeGuard":null,"tryCatchPattern":"const res = await fetch('/api/oauth/logout', {...});\nif (res.status === 500) {\n  const { message } = await res.json();\n  if (/delete credential/i.test(message ?? '')) {\n    // store file corrupt or unwritable — recover by backing it up and retrying once\n    await backupAndResetCredentialStore();\n    return fetch('/api/oauth/logout', {...});\n  }\n  throw new Error(message);\n}","preventionTips":["Keep the credentials file valid JSON and writable by the backend user.","Back up before hand-editing the credential store.","Remember logout requires rewriting the store — a read-only file blocks logout, not just login."],"tags":["oauth","http-500","credentials","filesystem","logout"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}