argoproj/argo-workflows · error
os create %s: %w
Error message
os create %s: %w
What it means
After successfully opening the GCS reader, downloadObject creates the local file with os.Create(localPath). This error wraps an os.Create failure, i.e. the local artifact file could not be opened for writing even though its directory exists.
Source
Thrown at workflow/artifacts/gcs/gcs.go:176
relObjPath := strings.TrimPrefix(objName, objPrefix)
localPath := filepath.Join(path, relObjPath)
objectDir, _ := filepath.Split(localPath)
if objectDir != "" {
if err := os.MkdirAll(objectDir, 0o700); err != nil {
return fmt.Errorf("mkdir %s: %w", objectDir, err)
}
}
rc, err := client.Bucket(bucket).Object(objName).NewReader(ctx)
if err != nil {
if errors.Is(err, storage.ErrObjectNotExist) {
return argoerrors.New(argoerrors.CodeNotFound, err.Error())
}
return fmt.Errorf("new bucket reader: %w", err)
}
defer rc.Close()
out, err := os.Create(localPath)
if err != nil {
return fmt.Errorf("os create %s: %w", localPath, err)
}
defer func() {
if closeErr := out.Close(); closeErr != nil {
logger := logging.RequireLoggerFromContext(ctx)
logger.WithField("path", localPath).WithError(closeErr).Error(ctx, "Error closing file")
}
}()
_, err = io.Copy(out, rc)
if err != nil {
return fmt.Errorf("io copy: %w", err)
}
return nil
}
// list all the object names of the prefix in the bucket
func listByPrefix(ctx context.Context, client *storage.Client, bucket, prefix, delim string) ([]string, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()View on GitHub (pinned to 35bff19146)
Solutions
- Ensure the local path doesn't clash with an existing directory (check the GCS key names for trailing-slash objects)
- Grant the container user write access: securityContext.runAsUser + fsGroup matching the volume, or chmod the mount
- Shorten the artifact path or the GCS key names
- Check volume/filesystem health
Example fix
// before
securityContext: {runAsUser: 1000} # volume owned by root, mode 755
// after
securityContext: {runAsUser: 1000, fsGroup: 1000} # or add initContainer chmod on emptyDir Defensive patterns
Strategy: validation
Validate before calling
import "os"
func canCreate(path string) error {
if fi, err := os.Lstat(path); err == nil && fi.IsDir() {
return fmt.Errorf("%s is a directory", path)
}
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil { return err }
return f.Close()
} Try / catch
if err := download(ctx, art, path); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && os.IsPermission(pe) {
// fix runAsUser/fsGroup or chmod the volume before retry
}
return err
} Prevention
- Match securityContext.runAsUser/fsGroup with volume ownership
- Avoid GCS keys ending in '/' that materialize as local directories
- Keep artifact paths short and under writable mounts like /tmp
When it happens
Trigger: os.Create fails on localPath: path exists as a directory, permission denied for the container user, path too long (ENAMETOOLONG), or filesystem/I/O error on the output volume.
Common situations: GCS object whose name ends with '/' produced a directory at localPath; running as non-root (runAsUser) without write access to /tmp or the output volume; artifact path exceeding filesystem name limits.
Related errors
- failed to create parent directory for artifact %q at %s: %w
- failed to clear existing path for artifact %q at %s: %w
- unable to remove attempted file download %s: %w
- mkdir %s: %w
- new bucket reader: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/63b19f22478e3072.
Report an issue: GitHub.