GoogleContainerTools/skaffold · error

reading .dockerignore: %w

Error message

reading .dockerignore: %w

What it means

SyncMap fails with "reading .dockerignore" when readDockerignore cannot read the .dockerignore file that sits next to the resolved Dockerfile. Skaffold treats a present-but-unreadable .dockerignore as fatal for computing syncable files (a missing file is usually ignored; IO errors like permission denied are not).

Source

Thrown at pkg/skaffold/docker/syncmap.go:47

// SyncMap creates a map of syncable files by looking at the COPY/ADD commands in the Dockerfile.
// All keys are relative to the Skaffold root, the destinations are absolute container paths.
// TODO(corneliusweig) destinations are not resolved across stages in multistage dockerfiles. Is there a use-case for that?
func SyncMap(ctx context.Context, workspace string, dockerfilePath string, buildArgs map[string]*string, cfg Config) (map[string][]string, error) {
	absDockerfilePath, err := NormalizeDockerfilePath(workspace, dockerfilePath)
	if err != nil {
		return nil, fmt.Errorf("normalizing dockerfile path: %w", err)
	}

	// only the COPY/ADD commands from the last image are syncable
	fts, err := ReadCopyCmdsFromDockerfile(ctx, true, absDockerfilePath, workspace, buildArgs, cfg)
	if err != nil {
		return nil, err
	}

	excludes, err := readDockerignore(workspace, absDockerfilePath)
	if err != nil {
		return nil, fmt.Errorf("reading .dockerignore: %w", err)
	}

	srcByDest, err := walkWorkspaceWithDestinations(workspace, excludes, fts)
	if err != nil {
		return nil, fmt.Errorf("walking workspace: %w", err)
	}

	return invertMap(srcByDest), nil
}

// walkWorkspaceWithDestinations walks the given host directories and determines their
// location in the container. It returns a map of host path by container destination.
// Note: if you change this function, you might also want to modify `WalkWorkspace`.
func walkWorkspaceWithDestinations(workspace string, excludes []string, fts []FromTo) (map[string]string, error) {
	dockerIgnored, err := NewDockerIgnorePredicate(workspace, excludes)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to a1189de023)

Solutions

  1. Check that .dockerignore next to the Dockerfile is a regular file: `file .dockerignore`; delete it if it's a directory.
  2. Fix permissions: `chmod u+r .dockerignore` or chown to the running user.
  3. Remove or repair broken symlinks pointing at missing targets.
  4. If the file is unnecessary, delete it so readDockerignore takes the missing-file path.
  5. Verify disk health/space if the error indicates an underlying IO failure.

Example fix

// before
$ ls -l .dockerignore
drwxr-xr-x 2 root root .dockerignore   # accidentally a directory
// after
$ rm -rf .dockerignore && printf 'node_modules\n.git\n' > .dockerignore && chmod u+r .dockerignore
Defensive patterns

Strategy: validation

Validate before calling

func checkDockerignore(workspace, dockerfilePath string) error {
	dir := filepath.Dir(dockerfilePath)
	if !filepath.IsAbs(dir) { dir = filepath.Join(workspace, dir) }
	p := filepath.Join(dir, ".dockerignore")
	fi, err := os.Stat(p)
	if os.IsNotExist(err) { return nil } // absent is fine
	if err != nil { return err }
	if !fi.Mode().IsRegular() { return fmt.Errorf("%s is not a regular file", p) }
	f, err := os.Open(p)
	if err != nil { return fmt.Errorf("%s unreadable: %w", p, err) }
	f.Close()
	return nil
}

Type guard

func dockerignoreReadable(workspace, df string) bool { return checkDockerignore(workspace, df) == nil }

Try / catch

if _, err := docker.SyncMap(ctx, workspace, dockerfilePath, buildArgs, cfg); err != nil {
	if strings.Contains(err.Error(), "reading .dockerignore") {
		return fmt.Errorf("ensure .dockerignore next to %s is a readable regular file (or delete it): %v", dockerfilePath, err)
	}
	return err
}

Prevention

When it happens

Trigger: A .dockerignore exists next to the Dockerfile but is a directory, has no read permission, or an IO error occurs while opening/reading it during SyncMap.

Common situations: .dockerignore created as a directory by mistake; restrictive permissions after copying files in a container/CI with root ownership; a symlink pointing to a nonexistent target; disk/IO failures.

Related errors


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