GoogleContainerTools/skaffold · error

finding changed file %s relative to context %q: %w

Error message

finding changed file %s relative to context %q: %w

What it means

Skaffold's inferred file sync (inferredSyncItem) computes each changed/added file's path relative to the artifact's workspace so it can match it against the `sync.infer` patterns. When filepath.Rel(workspace, file) fails, the file cannot be expressed relative to the build context, and Skaffold wraps and returns the underlying error with this message.

Source

Thrown at pkg/skaffold/sync/sync.go:139

			Delete:   toDelete,
		}, nil
	}

	// deleted files are no longer contained in the syncMap, so we need to rebuild
	if len(e.Deleted) > 0 {
		return nil, nil
	}

	syncMap, err := SyncMap(ctx, a, cfg)
	if err != nil {
		return nil, fmt.Errorf("inferring syncmap for image %q: %w", a.ImageName, err)
	}

	toCopy := make(map[string][]string)
	for _, f := range append(e.Modified, e.Added...) {
		relPath, err := filepath.Rel(a.Workspace, f)
		if err != nil {
			return nil, fmt.Errorf("finding changed file %s relative to context %q: %w", f, a.Workspace, err)
		}

		matches := false
		for _, p := range a.Sync.Infer {
			matches, err = doublestar.PathMatch(filepath.FromSlash(p), relPath)
			if err != nil {
				return nil, fmt.Errorf("pattern error for %q: %w", relPath, err)
			}
			if matches {
				break
			}
		}
		if !matches {
			log.Entry(ctx).Infof("Changed file %s does not match any sync pattern. Skipping sync", relPath)
			return nil, nil
		}

		if dsts, ok := syncMap[relPath]; ok {

View on GitHub (pinned to a1189de023)

Solutions

  1. Verify the artifact's `workspace:` in skaffold.yaml is the absolute real path (resolve symlinks) containing the watched files
  2. Ensure the file watcher is started with the same absolute path passed as the artifact workspace
  3. Fix path casing/slash normalization (use filepath.FromSlash/Clean consistently) before calling NewItem

Example fix

// before
workspace: ./service   # relative, may resolve differently from watcher
// after
workspace: /home/dev/project/service  # absolute, symlink-resolved
Defensive patterns

Strategy: validation

Validate before calling

ws, err := filepath.EvalSymlinks(a.Workspace)
if err != nil { return err }
for _, f := range append(e.Modified, e.Added...) {
    if _, err := filepath.Rel(ws, f); err != nil {
        return fmt.Errorf("file %s not under workspace %s", f, ws)
    }
}

Type guard

func fileInWorkspace(ws, f string) bool {
    rel, err := filepath.Rel(ws, f)
    return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator))
}

Try / catch

item, err := sync.NewItem(ctx, a, tag, e, cfg, builds)
if err != nil {
    if strings.Contains(err.Error(), "relative to context") {
        log.Warnf("skipping sync for out-of-workspace file: %v", err)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: A file event arrives whose absolute path is not underneath a.Workspace (e.g. event paths with different volume spellings, symlinked workspaces, or a workspace configured as a relative/nonexistent path), making filepath.Rel return an error.

Common situations: Watching files via a symlinked directory while skaffold.yaml names the real path; running on a case-insensitive filesystem where casing differs between config and events; Docker/K8s file-watch events referencing temp files outside the workspace.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/edd3f403f1ad6cfe. Report an issue: GitHub.