dagger/dagger · error
failed to change mode: %w
Error message
failed to change mode: %w
What it means
Returned by rewriteMetadata in engine/filesync/localfs.go when os.Chmod(p, mode) fails while restoring a synced file's permission bits (skipped for symlinks), and re-wrapped by WriteFile as 'failed to rewrite file metadata'. The file data is already written; the library aborts because the copy's permissions would otherwise diverge from the source.
Source
Thrown at engine/filesync/localfs.go:1066
if err := verifyExpectedChange(path, appliedChange.result(), expectedChangeKind, upperStat); err != nil {
appliedChange.release()
return nil, 0, err
}
return appliedChange, writtenBytes, nil
}
func (local *localFS) Walk(ctx context.Context, path string, walkFn fs.WalkDirFunc) error {
return local.filterFS.Walk(ctx, path, walkFn)
}
func rewriteMetadata(p string, upperStat *types.Stat) error {
if err := os.Lchown(p, int(upperStat.Uid), int(upperStat.Gid)); err != nil {
return fmt.Errorf("failed to change owner: %w", err)
}
if os.FileMode(upperStat.Mode)&os.ModeSymlink == 0 {
if err := os.Chmod(p, os.FileMode(upperStat.Mode)); err != nil {
return fmt.Errorf("failed to change mode: %w", err)
}
}
var utimes [2]unix.Timespec
utimes[0] = unix.NsecToTimespec(upperStat.ModTime)
utimes[1] = utimes[0]
if err := unix.UtimesNanoAt(unix.AT_FDCWD, p, utimes[0:], unix.AT_SYMLINK_NOFOLLOW); err != nil {
return fmt.Errorf("failed to call utimes: %w", err)
}
return nil
}
// Check that the change applied by mutating methods is actually the one we thought we were applying. If not, the client
// filesystem changed during the sync and we need to error out to avoid inconsistencies.
func verifyExpectedChange(path string, appliedChange *ChangeWithStat, expectedKind ChangeKind, expectedStat *types.Stat) error {
if appliedChange.kind == ChangeKindDelete || expectedKind == ChangeKindDelete {View on GitHub (pinned to 82ba2681db)
Solutions
- Run as root or ensure the process retains ownership of the file so chmod is permitted (check ordering of chown/chmod when unprivileged)
- Clear immutable flags on the destination: chattr -i <file> (or remove the read-only mount option)
- Confirm the destination filesystem supports POSIX permissions; avoid FAT/exFAT targets or remount with proper support
- Check the wrapped errno in the error chain to distinguish EPERM (permissions) from EROFS (read-only fs) and fix accordingly
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the process owns the file it is about to chmod and fs is not read-only
info, err := os.Lstat(p)
if err != nil { return err }
if os.Geteuid() != 0 && info.Sys().(*syscall.Stat_t).Uid != uint32(os.Geteuid()) {
return fmt.Errorf("process does not own %s; chmod will EPERM", p)
}
if err := unix.Access(dir, unix.W_OK); err != nil {
return fmt.Errorf("destination not writable (read-only fs?): %w", err)
} Try / catch
err := rewriteMetadata(p, upperStat)
var pathErr *os.PathError
if errors.As(err, &pathErr) && errors.Is(pathErr.Err, syscall.EPERM) {
// un-chown first / run as root, or chattr -i the target, then retry
} else if errors.As(err, &pathErr) && errors.Is(pathErr.Err, syscall.EROFS) {
// remount destination read-write
} Prevention
- When unprivileged, avoid chowning files to other users before chmod (chown-then-chmod ordering breaks EPERM-free flow)
- Clear immutable attributes (chattr -i) on destination files before re-syncing
- Use destination filesystems that support permission bits (not FAT/exFAT)
- Ensure the destination mount is not read-only for the engine's user
When it happens
Trigger: os.Chmod on the freshly written file fails — typically EPERM because the process doesn't own the file (e.g. a preceding Lchown changed ownership to another user in a non-root context) or an immutable/read-only attribute is set; also possible on filesystems that don't support permission bits.
Common situations: Non-root flow where Lchown succeeded to a different owner making the process no longer the file owner (EPERM on chmod); files with chattr +i / immutable flag on the destination; FAT/exFAT or restricted NFS mounts; read-only bind mounts.
Related errors
- failed to chmod temp dir: %w
- chmod resolv.conf: %w
- chmod hosts file: %w
- failed to chmod path %q during apply
- failed to restrict session directory permissions: %w
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/c70b1cac4ba02002.
Report an issue: GitHub.