dagger/dagger · error
file name %q must not contain a directory
Error message
file name %q must not contain a directory
What it means
FileBlobLazy.Evaluate validates that the filename given to a blob-backed file (created via an API like File.withNewFile/with blob contents) contains no path separators. A blob file must be a single entry at the snapshot root; passing something like "sub/dir/file.txt" makes the lazy evaluation fail immediately. This is a direct input-validation error raised at evaluation time.
Source
Thrown at core/file.go:501
case persistedFileLazyKindChown:
var persisted persistedFileChownLazy
if err := json.Unmarshal(payload, &persisted); err != nil {
return nil, fmt.Errorf("decode persisted file chown lazy: %w", err)
}
parent, err := loadPersistedObjectResultByResultID[*File](ctx, dag, persisted.ParentResultID, "file chown parent")
if err != nil {
return nil, err
}
return &FileChownLazy{LazyState: NewLazyState(), Parent: parent, Owner: persisted.Owner}, nil
default:
return nil, fmt.Errorf("decode persisted file lazy payload: unsupported lazy kind %q", lazyKind)
}
}
func (lazy *FileBlobLazy) Evaluate(ctx context.Context, file *File) error {
return lazy.LazyState.Evaluate(ctx, "File.blob", func(ctx context.Context) error {
if dir, _ := filepath.Split(lazy.Filename); dir != "" {
return fmt.Errorf("file name %q must not contain a directory", lazy.Filename)
}
if err := ValidateFileName(lazy.Filename); err != nil {
return err
}
permissions := lazy.Permissions
if permissions == 0 {
permissions = 0o644
}
query, err := CurrentQuery(ctx)
if err != nil {
return err
}
scratch, err := query.SnapshotManager().Scratch(ctx)
if err != nil {
return fmt.Errorf("create blob scratch snapshot: %w", err)
}
newRef, err := query.SnapshotManager().New(View on GitHub (pinned to 82ba2681db)
Solutions
- Pass only the base filename, then place the file in a directory using Directory.withNewFile instead of File APIs
- Strip or split off the directory component before the call
- Create the file via `directory.WithNewDirectory("docs").WithNewFile("readme.md", contents)`
- Validate names client-side with ValidateFileName before calling the API
Example fix
// before
file := dir.WithNewFile("docs/readme.md", contents)
// after
dir = dir.WithNewDirectory("docs")
file := dir.WithNewFile("readme.md", contents) Defensive patterns
Strategy: validation
Validate before calling
func validBlobName(name string) error {
if dir, _ := filepath.Split(name); dir != "" {
return fmt.Errorf("name %q must not contain a directory", name)
}
return ValidateFileName(name)
}
// call before: validBlobName("readme.md") instead of "docs/readme.md" Type guard
func isBareFileName(name string) bool {
dir, _ := filepath.Split(name)
return dir == "" && name != "" && name != "." && name != ".."
} Try / catch
f, err := dir.WithNewFile(name, contents)
if err != nil && strings.Contains(err.Error(), "must not contain a directory") {
dir = dir.WithNewDirectory(filepath.Dir(name))
f, err = dir.WithNewFile(filepath.Base(name), contents)
} Prevention
- Only pass base filenames to file-with-content APIs; use Directory APIs for paths
- Split joined paths and assert the dir component is empty before calling
- Use ValidateFileName client-side in codegen or wrappers
- Document path vs name semantics in internal helpers
When it happens
Trigger: Creating a new blob file where the name argument contains a directory component (filepath.Split yields a non-empty dir), e.g. `file.withNewFile("docs/readme.md", contents)`.
Common situations: Users assuming withNewFile accepts relative paths inside the container; porting code that built nested paths; generating filenames by joining path fragments; templates that interpolate full paths.
Related errors
- path %s is a file, not a directory
- workdir path %q escapes workdir
- workspace working directory %q must be a relative path withi
- cannot mount over the workspace root
- cannot expand home directory
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/decc9ff18531c67a.
Report an issue: GitHub.