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

  1. Confirm the project working directory exists and is traversable: ls -ld on each path component of the directory shown in the error.
  2. Restore or recreate the working directory (git checkout / re-clone) if it was deleted, then rerun watch.
  3. Fix or remove broken symlinks in the directory chain (readlink -f <dir>).
  4. 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

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


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/d25f0f8b7a243308. Report an issue: GitHub.