{"record":{"id":"4e6bb90d6093fc87","repo":"gofiber/fiber","slug":"failed-to-check-directory-w","errorCode":null,"errorMessage":"failed to check directory: %w","messagePattern":"failed to check directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/response.go","lineNumber":195,"sourceCode":"\t}\n\n\treturn r.client.xmlUnmarshal(r.Body(), v)\n}\n\n// Save writes the response body to a file or io.Writer.\n// If a string path is provided, it creates directories if needed, then writes to a file.\n// If an io.Writer is provided, it writes directly to it.\n// When streaming is enabled, the body is read directly from the stream.\nfunc (r *Response) Save(v any) error {\n\tswitch p := v.(type) {\n\tcase string:\n\t\tfile := filepath.Clean(p)\n\t\tdir := filepath.Dir(file)\n\n\t\t// Create directory if it doesn't exist\n\t\tif _, err := os.Stat(dir); err != nil {\n\t\t\tif !errors.Is(err, fs.ErrNotExist) {\n\t\t\t\treturn fmt.Errorf(\"failed to check directory: %w\", err)\n\t\t\t}\n\n\t\t\tif err = os.MkdirAll(dir, 0o750); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create directory: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\t// Create and write to file\n\t\toutFile, err := os.Create(file)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create file: %w\", err)\n\t\t}\n\t\tdefer func() { _ = outFile.Close() }() //nolint:errcheck // not needed\n\n\t\t// Use BodyStream() which handles both streaming and non-streaming cases\n\t\tif _, err = io.Copy(outFile, r.BodyStream()); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write response body to file: %w\", err)\n\t\t}","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/client/response.go#L177-L213","documentation":"Response.Save, given a string path, first calls os.Stat on the target directory. This wraps any Stat error that is NOT fs.ErrNotExist — i.e. a permission denial, a path that names a file (not a dir), or another OS-level obstruction. Not-exist is handled separately by attempting MkdirAll.","triggerScenarios":"The parent path exists but is not searchable (permission denied); the parent 'directory' is actually a regular file; a symlink loop; filesystem-level error (NFS stale handle).","commonSituations":"Process UID lacks execute permission on a parent dir; user-supplied save path that collides with an existing file's name; broken symlinks in the path.","solutions":["Check the wrapped cause: EACCES implies permissions, ENOTDIR implies a path component is a file.","Pre-validate the directory with os.Stat and surface a clearer error to the caller.","Run the process with appropriate filesystem permissions or write to a directory it owns."],"exampleFix":"// before\nif err := resp.Save(userPath); err != nil { ... }\n\n// after\ndir := filepath.Dir(filepath.Clean(userPath))\nif info, err := os.Stat(dir); err != nil || !info.IsDir() {\n    return fmt.Errorf(\"save dir invalid: %w\", err)\n}\nif err := resp.Save(userPath); err != nil { ... }","handlingStrategy":"validation","validationCode":"dir := filepath.Dir(filepath.Clean(p))\ninfo, err := os.Stat(dir)\nif err == nil && !info.IsDir() {\n    return fmt.Errorf(\"%s is not a directory\", dir)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-validate the parent directory before calling Save.","Run the process with execute rights on parent directories.","Avoid path components that are regular files."],"tags":["client","response","save","filesystem","permissions"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}