gofiber/fiber · error
failed to check directory
Error message
failed to check directory: %w
What it means
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.
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.
Example fix
// before
if err := resp.Save(userPath); err != nil { ... }
// after
dir := filepath.Dir(filepath.Clean(userPath))
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
return fmt.Errorf("save dir invalid: %w", err)
}
if err := resp.Save(userPath); err != nil { ... } Defensive patterns
Strategy: validation
Validate before calling
dir := filepath.Dir(filepath.Clean(p))
info, err := os.Stat(dir)
if err == nil && !info.IsDir() {
return fmt.Errorf("%s is not a directory", dir)
} Prevention
- Pre-validate the parent directory before calling Save.
- Run the process with execute rights on parent directories.
- Avoid path components that are regular files.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- failed to create directory
- failed to create file
- failed to write response body to file
- failed to write response body to writer
- open file error
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/4e6bb90d6093fc87.
Report an issue: GitHub.
Appendix: source
Thrown at client/response.go:195
}
return r.client.xmlUnmarshal(r.Body(), v)
}
// Save writes the response body to a file or io.Writer.
// If a string path is provided, it creates directories if needed, then writes to a file.
// If an io.Writer is provided, it writes directly to it.
// When streaming is enabled, the body is read directly from the stream.
func (r *Response) Save(v any) error {
switch p := v.(type) {
case string:
file := filepath.Clean(p)
dir := filepath.Dir(file)
// Create directory if it doesn't exist
if _, err := os.Stat(dir); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("failed to check directory: %w", err)
}
if err = os.MkdirAll(dir, 0o750); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
}
// Create and write to file
outFile, err := os.Create(file)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer func() { _ = outFile.Close() }() //nolint:errcheck // not needed
// Use BodyStream() which handles both streaming and non-streaming cases
if _, err = io.Copy(outFile, r.BodyStream()); err != nil {
return fmt.Errorf("failed to write response body to file: %w", err)
}View on GitHub (pinned to a105acad6c)