GoogleContainerTools/skaffold · error
opening %s: %w
Error message
opening %s: %w
What it means
In the v2 event package, after directory creation succeeds, SaveEventsToFile opens the file with O_APPEND|O_WRONLY|O_CREATE (mode 0600). Failures are wrapped as 'opening %s'. The directory exists at this point, so the problem is with the file itself.
Source
Thrown at pkg/skaffold/event/v2/event.go:322
}
ev.state.DebuggingContainers = ev.state.DebuggingContainers[:n]
}
ev.stateLock.Unlock()
}
ev.logEvent(event)
}
// SaveEventsToFile saves the current event log to the filepath provided
func SaveEventsToFile(fp string) error {
handler.logLock.Lock()
// Ensure that the filepath provided has the directories available when attemping to save the file.
dir := filepath.Dir(fp)
if err := os.MkdirAll(dir, 0700); err != nil {
return fmt.Errorf("unable to create directory %q: %w", dir, err)
}
f, err := os.OpenFile(fp, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("opening %s: %w", fp, err)
}
defer f.Close()
marshaller := jsonpb.Marshaler{}
for _, ev := range handler.eventLog {
contents := bytes.NewBuffer([]byte{})
if err := marshaller.Marshal(contents, ev); err != nil {
return fmt.Errorf("marshalling event: %w", err)
}
if _, err := f.WriteString(contents.String() + "\n"); err != nil {
return fmt.Errorf("writing string: %w", err)
}
}
handler.logLock.Unlock()
return nil
}
// SaveLastLog writes the output from the previous run to the specified filepath
func SaveLastLog(fp string) error {View on GitHub (pinned to a1189de023)
Solutions
- Append a filename if the destination is a directory path
- Check that the filesystem is not read-only (mount | grep <path>) and pick a writable location
- Verify write permission on the directory for the current user (ls -ld)
- Inspect the wrapped syscall error for the precise errno
Example fix
// before
v2event.SaveEventsToFile("/mnt/ro-volume/logs/")
// after
v2event.SaveEventsToFile("/mnt/rw-volume/logs/events.json") Defensive patterns
Strategy: fallback
Validate before calling
if info, err := os.Stat(fp); err == nil && info.IsDir() {
return fmt.Errorf("v2 event log path %q is a directory", fp)
} Try / catch
if err := v2event.SaveEventsToFile(fp); err != nil {
if strings.Contains(err.Error(), "opening ") {
fallback := filepath.Join(os.TempDir(), "skaffold-v2-events.json")
log.Warnf("falling back to %s: %v", fallback, err)
return v2event.SaveEventsToFile(fallback)
}
return err
} Prevention
- Always pass a complete file path including a filename
- Prefer writable locations: os.TempDir() or an explicitly mounted volume
- Confirm the filesystem is mounted read-write before saving
- Check for MAC/security policies (SELinux, AppArmor) blocking file creation in the target directory
When it happens
Trigger: The target path resolves to an existing directory (EISDIR); the parent is unwritable despite existing (EACCES); path exceeds NAME_MAX; the target is on a read-only filesystem where MkdirAll succeeded because the directory already existed but creating a new file inside fails (EROFS).
Common situations: Passing a directory path without a filename; on read-only filesystems where the directory already exists so MkdirAll succeeds but opening a new file fails; SELinux/AppArmor policies blocking creation.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- opening %s: %w
- unable to create directory %q: %w
- unable to create directory %q: %w
- writing string: %w
- writing string: %w
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/06487bdebe5b51d8.
Report an issue: GitHub.