navidrome/navidrome · error
'%s' is a directory
Error message
'%s' is a directory
What it means
fileExists is Navidrome's pre-flight check before invoking ffmpeg on a path (Transcode, ExtractImage, ProbeAudioStream). It stats the path and, if it exists but is a directory, returns this plain error naming the path. ffmpeg itself can't transcode a directory, so the library rejects it early with a clear message instead of an ffmpeg failure.
Source
Thrown at core/ffmpeg/ffmpeg.go:142
func (e *ffmpeg) ExtractImage(ctx context.Context, path string) (io.ReadCloser, error) {
if _, err := ffmpegCmd(); err != nil {
return nil, err
}
if err := fileExists(path); err != nil {
return nil, err
}
args := createFFmpegCommand(extractImageCmd, path, 0, 0)
return e.start(ctx, args)
}
func fileExists(path string) error {
s, err := os.Stat(path)
if err != nil {
return err
}
if s.IsDir() {
return fmt.Errorf("'%s' is a directory", path)
}
return nil
}
func (e *ffmpeg) Probe(ctx context.Context, files []string) (string, error) {
if _, err := ffmpegCmd(); err != nil {
return "", err
}
args := createProbeCommand(probeCmd, files)
log.Trace(ctx, "Executing ffmpeg command", "args", args)
cmd := exec.CommandContext(ctx, args[0], args[1:]...) // #nosec
output, _ := cmd.CombinedOutput()
return string(output), nil
}
func (e *ffmpeg) ProbeAudioStream(ctx context.Context, filePath string) (*AudioProbeResult, error) {
if _, err := ffmpegCmd(); err != nil {
return nil, errView on GitHub (pinned to 4ed7494a32)
Solutions
- Fix the source record so the path points at an actual media file, not a folder
- Rescan the library to refresh stale/moved entries
- Check for name collisions where a directory replaced a file of the same name
- Inspect the path in the error: strip the trailing directory or append the real filename
- If a symlink resolves to a directory, point it at the file instead
Example fix
// before opts.FilePath = "/music/Artist/Album" // directory // after opts.FilePath = "/music/Artist/Album/01 track.flac"
Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(filePath)
if err != nil { return err }
if info.IsDir() { return fmt.Errorf("%q is a directory, expected a media file", filePath) }
if !info.Mode().IsRegular() { return fmt.Errorf("%q is not a regular file", filePath) } Type guard
func isPlayableFile(path string) bool {
info, err := os.Stat(path)
return err == nil && info.Mode().IsRegular()
} Try / catch
rc, err := ffmpeg.Transcode(ctx, opts)
if err != nil {
if strings.Contains(err.Error(), "is a directory") || statIsDir(opts.FilePath) {
// fix the path/DB record before retrying
}
return err
} Prevention
- Always pass the full path to a media file, never a folder
- Rescan the library after moving files so DB paths stay valid
- Guard against name collisions between files and directories
- Validate paths coming from playlists or external integrations
When it happens
Trigger: Calling Transcode/ExtractImage/ProbeAudioStream with TranscodeOptions.FilePath (or path) pointing to a directory: a DB media_file record whose Path column holds a folder (bad scan/import), or constructing the path by joining a folder where a filename was expected.
Common situations: Library scanned while files were mid-move leaving folder-only entries; custom integrations passing a folder path from a playlist; a file and directory sharing the same name after a re-organize; broken import scripts writing directory paths into the DB.
Related errors
- artist folder '%s' is outside library '%s'
- no matches for '%s' in '%s' or its parent directories (withi
- no matches for '%s' in '%s'
- creating image directory: %w
- creating image file: %w
AI-assisted analysis of navidrome/navidrome@4ed7494a32 (2026-09-01).
Data as JSON: /api/errors/2f92c13ebbd7f3d8.
Report an issue: GitHub.