GoogleContainerTools/skaffold · error
%s is a directory
Error message
%s is a directory
What it means
AbsFile resolves a filename against a workspace and returns its absolute path, but first stats the path and refuses directories with this error. It is thrown when the provided filename resolves to a directory rather than a regular file. CreateCommand (and tests) use it to validate executable/file inputs before launching commands.
Source
Thrown at pkg/skaffold/util/util.go:207
m[v[0]] = v[1]
}
}
return m
}
func isAlphaNum(c uint8) bool {
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
}
// AbsFile resolves the absolute path of the file named filename in directory workspace, erroring if it is not a file
func AbsFile(workspace string, filename string) (string, error) {
file := filepath.Join(workspace, filename)
info, err := os.Stat(file)
if err != nil {
return "", err
}
if info.IsDir() {
return "", fmt.Errorf("%s is a directory", file)
}
return filepath.Abs(file)
}
// NonEmptyLines scans the provided input and returns the non-empty strings found as an array
func NonEmptyLines(input []byte) []string {
var result []string
scanner := bufio.NewScanner(bytes.NewReader(input))
for scanner.Scan() {
if line := scanner.Text(); len(line) > 0 {
result = append(result, line)
}
}
return result
}
// CloneThroughJSON clones an `old` object into a `new` one
// using json marshalling and unmarshalling.View on GitHub (pinned to a1189de023)
Solutions
- Point the configuration at the actual file, not its containing directory (e.g. ./script.sh instead of ./scripts).
- Verify the resolved path with `ls -la <workspace>/<filename>` — if it is a directory, append the filename.
- Check templating/variable substitution isn't dropping the filename portion of the path.
- If you intended a directory, use the appropriate config field for directories instead of one requiring a file.
Example fix
// before
cmd := util.CreateCommand(ctx, "/work/tools", "", []string{}) // /work/tools is a directory
// after
cmd := util.CreateCommand(ctx, "/work/tools/runner.sh", "", []string{}) Defensive patterns
Strategy: validation
Validate before calling
func mustBeFile(workspace, name string) error {
p := filepath.Join(workspace, name)
info, err := os.Stat(p)
if err != nil {
return err
}
if info.IsDir() {
return fmt.Errorf("%q is a directory; expected a file", p)
}
return nil
} Type guard
func isRegularFile(path string) bool {
info, err := os.Stat(path)
return err == nil && info.Mode().IsRegular()
} Try / catch
abs, err := util.AbsFile(workspace, name)
if err != nil {
if strings.HasSuffix(err.Error(), "is a directory") {
return fmt.Errorf("config points at directory %s; append the filename", err)
}
return err
} Prevention
- Point command/file config fields at concrete files, never directories.
- Verify paths with `ls -la` or os.Stat.IsDir() in pre-flight checks.
- Check templated paths render with the filename included.
- If a directory is intended, use the config field designed for directories.
When it happens
Trigger: Calling AbsFile (directly or via CreateCommand) with a path that os.Stat reports IsDir — e.g. pointing a command/file config at a directory name, or the workspace + filename join landing on a directory because a filename component was omitted.
Common situations: skaffold.yaml referencing a directory where a binary or file is expected (e.g. a custom test command pointing at a folder); a build artifact path that lost its filename during templating; typo making the path resolve to a directory.
Related errors
- reading image %q: %w
- failed creating remote cache directory: %w
- determining build workspace directory for image %v: %w
- %v is not a valid GCS path
- cannot add an empty image value
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/2cec98aebc4c0a1a.
Report an issue: GitHub.