slimtoolkit/slim · error
source is symlink
Error message
source is symlink
What it means
CopyToVolume copies a host directory/file into a Docker volume via a helper container. Before archiving, it checks whether the resolved source path is a symlink with fsutil.IsSymlink and refuses to proceed. This guard exists because copying a symlinked source could pull in unintended target content or escape the expected copy root, so the library treats symlinks as unsafe inputs.
Source
Thrown at pkg/docker/dockerutil/dockerutil.go:574
}
err = dclient.RemoveContainer(removeOptions)
if err != nil {
fmt.Printf("dockerutil.CopyToVolume: dclient.RemoveContainer() error = %v\n", err)
}
}
cleanSource, err := filepath.EvalSymlinks(source)
if err != nil {
log.Errorf("dockerutil.CopyToVolume: filepath.EvalSymlinks(%s) error = %v", source, err)
rmContainer()
return err
}
if fsutil.IsSymlink(cleanSource) {
log.Errorf("dockerutil.CopyToVolume: source is a symlink = %s", cleanSource)
rmContainer()
return fmt.Errorf("source is symlink")
}
tarData, err := archive.Tar(cleanSource, archive.Uncompressed)
if err != nil {
log.Errorf("dockerutil.CopyToVolume: archive.Tar() error = %v", err)
rmContainer()
return err
}
targetPath := volumeBasePath
if dstRootDir != "" {
dirData, err := GenStateDirsTar(dstRootDir, dstTargetDir)
if err != nil {
log.Errorf("dockerutil.CopyToVolume: GenStateDirsTar() error = %v", err)
rmContainer()
return err
}
View on GitHub (pinned to 81940d17fa)
Solutions
- Resolve the symlink first and pass the real path: use filepath.EvalSymlinks(cleanSource) and pass the resolved result to CopyToVolume.
- If symlink following is intended, copy the target content to a real directory (e.g. cp -rL) and use that directory as the source.
- If the symlink itself is the artifact to store, archive it manually and load it into the volume by other means instead of CopyToVolume.
Example fix
// before
err := dockerutil.CopyToVolume(ctx, "/tmp/mydata", volumeName)
// after
realPath, err := filepath.EvalSymlinks("/tmp/mydata")
if err != nil { return err }
err = dockerutil.CopyToVolume(ctx, realPath, volumeName) Defensive patterns
Strategy: validation
Validate before calling
import "path/filepath"
func isSymlink(p string) bool {
fi, err := os.Lstat(p)
return err == nil && fi.Mode()&os.ModeSymlink != 0
}
func resolveSource(p string) (string, error) {
if isSymlink(p) {
return filepath.EvalSymlinks(p)
}
return p, nil
}
real, err := resolveSource(source)
if err != nil { return err }
err = dockerutil.CopyToVolume(ctx, real, volume) Type guard
func isSymlinkPath(p string) bool {
fi, err := os.Lstat(p)
return err == nil && fi.Mode()&os.ModeSymlink != 0
} Try / catch
if _, err := dockerutil.CopyToVolume(ctx, src, vol); err != nil {
if strings.Contains(err.Error(), "source is symlink") {
real, rerr := filepath.EvalSymlinks(src)
if rerr != nil { return rerr }
return dockerutil.CopyToVolume(ctx, real, vol)
}
return err
} Prevention
- Run filepath.EvalSymlinks on user-supplied paths before passing them to CopyToVolume
- Reject or resolve symlinks early in any path-ingestion pipeline
- Watch for platform symlinks like /tmp -> /private/tmp on macOS
- Add a preflight Lstat check in wrappers around CopyToVolume
When it happens
Trigger: Calling dockerutil.CopyToVolume (directly or via DoArchiveState / CreateVolumeWithData) with a source path that resolves to a symlink, e.g. passing /var/run/docker.sock, a /tmp symlinked directory, or a user-supplied path that is a soft link.
Common situations: macOS/OSX temp dirs (/tmp -> /private/tmp) or user home paths that are symlinks; passing paths like /var/data/current that admins implemented as symlink switches; passing socket or device files that are symlinks into /proc or /run.
Related errors
AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31).
Data as JSON: /api/errors/a5658be2a2e33888.
Report an issue: GitHub.