GoogleContainerTools/skaffold · error

file pattern %s must match at least one file

Error message

file pattern %s must match at least one file

What it means

If a COPY source pattern matched zero files anywhere in the workspace, expandSrcGlobPatterns fails with 'file pattern %s must match at least one file'. Docker's build would also fail on such a COPY, so Skaffold surfaces it early during dependency computation. The message includes the full srcs list of the failing copy command.

Source

Thrown at pkg/skaffold/docker/parse.go:242

				return nil, fmt.Errorf("invalid glob pattern: %w", err)
			}
			if files == nil {
				continue
			}

			for _, f := range files {
				rel, err := filepath.Rel(workspace, f)
				if err != nil {
					return nil, fmt.Errorf("getting relative path of %s", f)
				}

				fts = append(fts, FromTo{From: rel, To: cpCmd.dest, ToIsDir: cpCmd.destIsDir, StartLine: cpCmd.startLine, EndLine: cpCmd.endLine})
			}
			matchesOne = true
		}

		if !matchesOne {
			return nil, fmt.Errorf("file pattern %s must match at least one file", cpCmd.srcs)
		}
	}

	log.Entry(context.TODO()).Debugf("Found dependencies for dockerfile: %v", fts)

	return fts, nil
}

func extractCopyCommands(ctx context.Context, nodes []*parser.Node, onlyLastImage bool, cfg Config) ([]*copyCommand, error) {
	stages := map[string]bool{
		"scratch": true,
	}

	slex := shell.NewLex('\\')
	var copied []*copyCommand

	workdir := "/"
	envs := make([]string, 0)

View on GitHub (pinned to a1189de023)

Solutions

  1. cd into the workspace and verify the path in the error actually exists (ls the pattern with the shell)
  2. Fix the COPY source path or glob in the Dockerfile to match the real file location
  3. Run the build/codegen step that generates the missing files before Skaffold computes dependencies
  4. Check filename case and workspace context in skaffold.yaml — the pattern is resolved relative to that context, not the repo root

Example fix

// before (Dockerfile)
COPY dist/bundle.js /app/
// after (path verified against workspace)
COPY build/output/bundle.js /app/
Defensive patterns

Strategy: validation

Validate before calling

func sourcesMatchAtLeastOneFile(workspace string, srcs []string) error {
    for _, s := range srcs {
        matches, err := filepath.Glob(filepath.Join(workspace, s))
        if err != nil { return err }
        if len(matches) == 0 {
            return fmt.Errorf("COPY source %q matches no files under %s", s, workspace)
        }
    }
    return nil
}

Type guard

func globMatchesSomething(workspace, pattern string) bool {
    matches, err := filepath.Glob(filepath.Join(workspace, pattern))
    return err == nil && len(matches) > 0
}

Try / catch

fts, err := skaffold.ReadCopyCmdsFromDockerfile(path, args, cfg, false)
if err != nil && strings.Contains(err.Error(), "must match at least one file") {
    var srcs string
    fmt.Sscanf(err.Error(), "file pattern %s must match", &srcs)
    return fmt.Errorf("verify path exists under workspace (case-sensitive) or run codegen first: %s", srcs)
}

Prevention

When it happens

Trigger: ReadCopyCmdsFromDockerfile (only when onlyLastImage/all-images mode requires existing files) hits a COPY whose source path/glob has no match on disk — wrong relative directory, file not built yet, case-mismatched filename, or pattern expecting generated output that does not exist yet.

Common situations: Case sensitivity (README.md vs readme.md on Linux); running dependency computation before a codegen step that produces the copied files; Dockerfile written for a different workspace layout than skaffold.yaml's context; typos or stale paths after refactoring.

Related errors


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