{"record":{"id":"94797b3468c0a669","repo":"router-for-me/CLIProxyAPI","slug":"failed-to-read-auth-file-w-94797b","errorCode":null,"errorMessage":"failed to read auth file: %w","messagePattern":"failed to read auth file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/auth_callbacks.go","lineNumber":250,"sourceCode":"\t}\n\treturn nil, fmt.Errorf(\"auth not found for auth_index %s\", authIndex)\n}\n\nfunc (h *Host) authPhysicalJSONByIndex(authIndex string) (*coreauth.Auth, []byte, error) {\n\tauth, errGet := h.authByIndex(authIndex)\n\tif errGet != nil {\n\t\treturn nil, nil, errGet\n\t}\n\tpath := strings.TrimSpace(authAttribute(auth, \"path\"))\n\tif path == \"\" {\n\t\treturn nil, nil, fmt.Errorf(\"auth file path not found for auth_index %s\", authIndex)\n\t}\n\tdata, errRead := os.ReadFile(path)\n\tif errRead != nil {\n\t\tif os.IsNotExist(errRead) {\n\t\t\treturn nil, nil, fmt.Errorf(\"auth file not found for auth_index %s\", authIndex)\n\t\t}\n\t\treturn nil, nil, fmt.Errorf(\"failed to read auth file: %w\", errRead)\n\t}\n\tif len(bytesTrimSpace(data)) == 0 {\n\t\treturn nil, nil, fmt.Errorf(\"auth file is empty for auth_index %s\", authIndex)\n\t}\n\tvar metadata map[string]any\n\tif errUnmarshal := json.Unmarshal(data, &metadata); errUnmarshal != nil {\n\t\treturn nil, nil, fmt.Errorf(\"invalid auth file for auth_index %s: %w\", authIndex, errUnmarshal)\n\t}\n\treturn auth, data, nil\n}\n\nfunc validateHostAuthSaveRequest(req pluginapi.HostAuthSaveRequest) (string, []byte, error) {\n\tname := strings.TrimSpace(req.Name)\n\tif isUnsafeAuthFileName(name) {\n\t\treturn \"\", nil, fmt.Errorf(\"invalid auth file name\")\n\t}\n\tif !strings.HasSuffix(strings.ToLower(name), \".json\") {\n\t\treturn \"\", nil, fmt.Errorf(\"auth file name must end with .json\")","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/auth_callbacks.go#L232-L268","documentation":"Wrapped I/O error from os.ReadFile in Host.authPhysicalJSONByIndex when reading the auth file fails for a reason other than not-existing (permissions, I/O error, path issues). The %w wrap preserves the underlying filesystem error for diagnosis.","triggerScenarios":"Auth file present but mode 0000 or owned by another user; reading during concurrent atomic replace (rename-on-write) hitting a transient moment; disk or NFS-level I/O failure; directory in place of the expected file.","commonSituations":"Running the proxy as a different user than the one that created auths/; files copied with restrictive umask; network-backed auth directory with intermittent I/O errors.","solutions":["Inspect the wrapped error (errors.Unwrap / errors.As to *fs.PathError) for the real cause","Fix ownership/permissions: the process user must read auths/<file>.json (chmod 600 with correct owner)","If it is a transient rename race, retry the read once after a short delay"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"var pathErr *fs.PathError\nif errors.As(err, &pathErr) {\n    switch {\n    case errors.Is(pathErr.Err, fs.ErrPermission):\n        // fix ownership/mode on the auth dir\n    default:\n        // log wrapped errno and retry once for transient I/O\n    }\n}","preventionTips":["Run the process as the user that owns auths/ with mode 600 files, 700 dir","Use atomic write-then-rename producers so readers never see half-states"],"tags":["go","pluginhost","auth","filesystem","permissions","io"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}