docker/compose · error
resolving symlink for %q: %w
Error message
resolving symlink for %q: %w
What it means
When normalizing legacy x-develop watch paths, compose resolves the project working directory through filepath.EvalSymlinks to make relative trigger paths absolute. If that call fails (directory deleted, permission denied along the path), this wrapped error is returned.
Source
Thrown at pkg/compose/watch.go:408
logrus.Debugf("batch complete: duration[%s] count[%d]", time.Since(start), len(batch))
}
}
}
func loadDevelopmentConfig(service types.ServiceConfig, project *types.Project) (*types.DevelopConfig, error) {
var config types.DevelopConfig
y, ok := service.Extensions["x-develop"]
if !ok {
return nil, nil
}
logrus.Warnf("x-develop is DEPRECATED, please use the official `develop` attribute")
err := mapstructure.Decode(y, &config)
if err != nil {
return nil, err
}
baseDir, err := filepath.EvalSymlinks(project.WorkingDir)
if err != nil {
return nil, fmt.Errorf("resolving symlink for %q: %w", project.WorkingDir, err)
}
for i, trigger := range config.Watch {
if !filepath.IsAbs(trigger.Path) {
trigger.Path = filepath.Join(baseDir, trigger.Path)
}
if p, err := filepath.EvalSymlinks(trigger.Path); err == nil {
// this might fail because the path doesn't exist, etc.
trigger.Path = p
}
trigger.Path = filepath.Clean(trigger.Path)
if trigger.Path == "" {
return nil, errors.New("watch rules MUST define a path")
}
if trigger.Action == types.WatchActionRebuild && service.Build == nil {
return nil, fmt.Errorf("service %s doesn't have a build section, can't apply %s on watch", types.WatchActionRebuild, service.Name)
}View on GitHub (pinned to ddc4b044b6)
Solutions
- Confirm the project working directory exists and is traversable: ls -ld on each path component of the directory shown in the error.
- Restore or recreate the working directory (git checkout / re-clone) if it was deleted, then rerun watch.
- Fix or remove broken symlinks in the directory chain (readlink -f <dir>).
- Migrate from deprecated x-develop to the official develop attribute, which reduces reliance on this resolution path.
Example fix
# before (deprecated, triggers EvalSymlinks on working dir)
services:
web:
x-develop:
watch:
- path: ./src
action: sync
# after (official attribute)
services:
web:
develop:
watch:
- path: ./src
target: /app/src
action: sync Defensive patterns
Strategy: validation
Validate before calling
func workingDirResolvable(dir string) bool {
_, err := filepath.EvalSymlinks(dir)
return err == nil
} Try / catch
if _, err := composeService.Watch(ctx, projectName, opts); err != nil {
if strings.Contains(err.Error(), "resolving symlink for") {
// verify/recreate the working directory, fix symlink chain, retry
}
return err
} Prevention
- Run watch from a stable, existing project directory (not temp dirs subject to cleanup).
- Migrate x-develop extensions to the official develop attribute.
- Ensure execute permission on every parent of the working directory.
When it happens
Trigger: Using a service with a legacy x-develop extension while the project's working directory cannot be resolved: it was removed after the project loaded, a parent directory lost execute permission, or the path passes through a broken symlink chain.
Common situations: Watching from a temp/checkout directory that another process deletes; macOS /tmp symlink edge cases; restricted environments where the working dir is not traversable; CI workspaces cleaned concurrently.
Related errors
- cannot take exclusive lock for project %q: %w
- --wait cannot be combined with --abort-on-container-exit, --
- --detach cannot be combined with --abort-on-container-exit,
- --no-build and --watch are incompatible
- failed to access env file %s: %w
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/d25f0f8b7a243308.
Report an issue: GitHub.