{"record":{"id":"7cd65f750daa7990","repo":"router-for-me/CLIProxyAPI","slug":"failed-to-write-file-w","errorCode":null,"errorMessage":"failed to write file: %w","messagePattern":"failed to write file: %w","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"internal/api/handlers/management/auth_files_crud.go","lineNumber":273,"sourceCode":"\tif err := h.writeAuthFile(ctx, name, data); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn name, nil\n}\n\nfunc (h *Handler) writeAuthFile(ctx context.Context, name string, data []byte) error {\n\tdst := filepath.Join(h.cfg.AuthDir, filepath.Base(name))\n\tif !filepath.IsAbs(dst) {\n\t\tif abs, errAbs := filepath.Abs(dst); errAbs == nil {\n\t\t\tdst = abs\n\t\t}\n\t}\n\tauth, err := h.buildAuthFromFileData(dst, data)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif errWrite := os.WriteFile(dst, data, 0o600); errWrite != nil {\n\t\treturn fmt.Errorf(\"failed to write file: %w\", errWrite)\n\t}\n\tif err := h.upsertAuthRecord(ctx, auth); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc requestedAuthFileNamesForDelete(c *gin.Context) ([]string, error) {\n\tif c == nil {\n\t\treturn nil, nil\n\t}\n\tnames := uniqueAuthFileNames(c.QueryArray(\"name\"))\n\tif len(names) > 0 {\n\t\treturn names, nil\n\t}\n\n\tbody, err := io.ReadAll(c.Request.Body)\n\tif err != nil {","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/api/handlers/management/auth_files_crud.go#L255-L291","documentation":"writeAuthFile in auth_files_crud.go persists an uploaded auth file with os.WriteFile(dst, data, 0o600) after building the Auth record. If the OS write to auths/<name> fails, it wraps as `failed to write file: %w`. The destination is constrained to h.cfg.AuthDir with a basename, so path traversal is already excluded — this is an environment-level write failure.","triggerScenarios":"authDir does not exist; directory or existing file not writable by the process user; disk full; SELinux denial; container read-only volume; existing file owned by root with 0600 while proxy runs unprivileged.","commonSituations":"Docker deployments mounting auths/ read-only or as root-owned volume; first run before authDir was created; disk quota exhausted; permission mismatch after migrating hosts.","solutions":["Ensure the configured authDir exists and is writable by the proxy process user (mkdir -p auths && chown).","Check disk space and quotas on the authDir filesystem.","On containers, mount auths/ as a writable volume with correct ownership.","Read the wrapped OS error for the precise errno (permission, ENOSPC, EROFS) and address that."],"exampleFix":"# before\n$ docker run -v ./auths:/app/auths:ro ...  # read-only mount\n\n# after\n$ docker run -v ./auths:/app/auths ...  # writable mount\n$ chown -R 1000:1000 ./auths","handlingStrategy":"validation","validationCode":"func authDirWritableForWrite(authDir string) bool {\n    if err := os.MkdirAll(authDir, 0o755); err != nil { return false }\n    probe := filepath.Join(authDir, \".probe\")\n    ok := os.WriteFile(probe, nil, 0o600) == nil\n    _ = os.Remove(probe)\n    return ok\n}","typeGuard":"func isAuthFileWriteFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to write file\")\n}","tryCatchPattern":null,"preventionTips":["Pre-flight check write access to authDir before enabling uploads.","Mount auths/ writable and owned by the process user in containers.","Monitor disk space on the authDir filesystem."],"tags":["management-api","filesystem","permissions","upload"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}