{"record":{"id":"478ccfa9683e2318","repo":"router-for-me/CLIProxyAPI","slug":"failed-to-update-auth-s-w","errorCode":null,"errorMessage":"failed to update auth %s: %w","messagePattern":"failed to update auth (.+?): %w","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"internal/api/handlers/management/auth_files_fields.go","lineNumber":148,"sourceCode":"\tif errWrite := setSourceAuthFileDisabled(sourcePath, disabled); errWrite != nil {\n\t\tif os.IsNotExist(errWrite) {\n\t\t\treturn errAuthFileNotFound\n\t\t}\n\t\treturn fmt.Errorf(\"failed to update source auth file: %w\", errWrite)\n\t}\n\tnow := time.Now()\n\tfor _, auth := range h.authManager.List() {\n\t\tif auth == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif !sameAuthFilePath(authAttribute(auth, \"path\"), sourcePath) &&\n\t\t\t!sameAuthFilePath(authAttribute(auth, coreauth.AttributeVirtualSource), sourcePath) {\n\t\t\tcontinue\n\t\t}\n\t\tapplyAuthDisabledState(auth, disabled)\n\t\tauth.UpdatedAt = now\n\t\tif _, errUpdate := h.authManager.Update(ctx, auth); errUpdate != nil {\n\t\t\treturn fmt.Errorf(\"failed to update auth %s: %w\", auth.ID, errUpdate)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc setSourceAuthFileDisabled(path string, disabled bool) error {\n\tpath = strings.TrimSpace(path)\n\tif path == \"\" {\n\t\treturn fmt.Errorf(\"source auth path is empty\")\n\t}\n\tdata, errRead := os.ReadFile(path)\n\tif errRead != nil {\n\t\treturn errRead\n\t}\n\tmetadata := make(map[string]any)\n\tif len(bytes.TrimSpace(data)) > 0 {\n\t\tif errUnmarshal := json.Unmarshal(data, &metadata); errUnmarshal != nil {\n\t\t\treturn fmt.Errorf(\"invalid auth file: %w\", errUnmarshal)","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/api/handlers/management/auth_files_fields.go#L130-L166","documentation":"After the source file's disabled flag was persisted, propagating the state to every runtime auth expanded from that source failed at authManager.Update for the auth named by %s. The file on disk and the in-memory registry can now be out of sync — the file says disabled but that one runtime auth record could not be updated.","triggerScenarios":"PATCH toggle on a plugin virtual source where one of its expanded child auths is concurrently deleted or rotated out between List() and Update(); a store-level failure (Postgres connection drop when using PGSTORE_*, file lock contention on the file store).","commonSituations":"Two admins toggling/rotating credentials simultaneously; background token refresh re-writing the auth concurrently; external store (PG/git/object store) briefly unavailable mid-operation.","solutions":["Retry the toggle request — re-running re-reads the file and re-applies state to all children idempotently","If using a remote store, check its connectivity/health and retry after it recovers","Avoid concurrent toggle and rotate/delete operations on the same source; sequence them","Verify final state via GET on the auth list and manually toggle again if any child drifted"],"exampleFix":"# before: concurrent\ncurl -X PATCH .../disable & curl -X POST .../rotate &\n# after: sequential\ncurl -X POST .../rotate && curl -X PATCH .../disable","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := patchPluginVirtualSourceStatus(ctx, auth, disabled)\nif err != nil && strings.Contains(err.Error(), \"failed to update auth\") {\n    // source file persisted; a retry reconciles runtime children idempotently\n    auth = refreshAuthFromManager(auth)\n    err = patchPluginVirtualSourceStatus(ctx, auth, disabled)\n}","preventionTips":["Do not run rotate/delete concurrently with enable-disable on the same source","Keep remote stores (PG/git) healthy before batch toggles","Verify final state with a list GET after toggling"],"tags":["concurrency","state-sync","auth","retryable"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}