{"record":{"id":"1afc6f19d41beaca","repo":"argoproj/argo-workflows","slug":"os-open-w","errorCode":null,"errorMessage":"os open: %w","messagePattern":"os open: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/gcs/gcs.go","lineNumber":313,"sourceCode":"\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"upload %s: %w\", dirName+relPath, err)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tobjectKey := normalizeGCSKey(filepath.Clean(key))\n\t\terr = uploadObject(ctx, client, bucket, objectKey, path)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"upload %s: %w\", path, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// upload an object to GCS\nfunc uploadObject(ctx context.Context, client *storage.Client, bucket, key, localPath string) error {\n\tf, err := os.Open(filepath.Clean(localPath))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"os open: %w\", err)\n\t}\n\tdefer func() {\n\t\tif closeErr := f.Close(); closeErr != nil {\n\t\t\tlogger := logging.RequireLoggerFromContext(ctx)\n\t\t\tlogger.WithField(\"path\", localPath).WithError(closeErr).Error(ctx, \"Error closing file\")\n\t\t}\n\t}()\n\twc := client.Bucket(bucket).Object(key).NewWriter(ctx)\n\tif _, err = io.Copy(wc, f); err != nil {\n\t\treturn fmt.Errorf(\"io copy: %w\", err)\n\t}\n\tif err := wc.Close(); err != nil {\n\t\treturn fmt.Errorf(\"writer close: %w\", err)\n\t}\n\treturn nil\n}\n\n// delete an object from GCS","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/gcs/gcs.go#L295-L331","documentation":"This error is thrown by uploadObject when os.Open fails on the local file being uploaded to GCS. It wraps the standard library *PathError (ENOENT, EACCES, EISDIR, etc.) and indicates the artifact driver could not even start reading the local artifact file — no bytes were sent to GCS. It is reached from uploadObjects when saving a single file artifact, or per-file when walking a directory artifact.","triggerScenarios":"uploadObject(ctx, client, bucket, key, localPath) with a localPath that does not exist, is a directory (when the isDir detection was bypassed or raced), or lacks read permission. Also occurs when the path contains characters removed by filepath.Clean or the file was deleted after listFileRelPaths enumerated a directory artifact.","commonSituations":"Workflow step failed to write its output before artifact saving started; artifact path points to a mounted volume that was unmounted; running argoexec as non-root against root-owned files; symlink pointing to a missing target; TOCTOU race between directory listing and file open on a changing output directory.","solutions":["Run `ls -la` / `stat` on the exact localPath from the error message inside the pod to confirm existence and permissions.","Fix the producing step so it writes the artifact file (check its exit code and logs) before artifact collection runs.","Correct permissions (`chmod`/`chown`) or run the executor with sufficient rights to read the artifact volume.","If uploading a directory, ensure nothing deletes/moves files concurrently during the workflow's artifact save phase.","Validate the artifact path in the workflow spec is absolute and matches the mounted volume mountPath."],"exampleFix":"// before: artifact declared but step never created it\n- name: out\n  path: /mnt/out/result.json\n// after: guarantee the file exists in the script\nscript:\n  command: [sh]\n  source: |\n    mkdir -p /mnt/out\n    echo \"{\\\"ok\\\": true}\" > /mnt/out/result.json","handlingStrategy":"validation","validationCode":"func assertReadable(path string) error {\n    info, err := os.Stat(path)\n    if err != nil {\n        return fmt.Errorf(\"artifact missing: %w\", err)\n    }\n    if info.IsDir() {\n        return fmt.Errorf(\"%s is a directory; Save handles dirs separately\", path)\n    }\n    f, err := os.Open(path)\n    if err != nil {\n        return fmt.Errorf(\"artifact unreadable: %w\", err)\n    }\n    return f.Close()\n}","typeGuard":"func isOSOpenErr(err error) (*fs.PathError, bool) {\n    var perr *fs.PathError\n    ok := errors.As(err, &perr) && strings.Contains(err.Error(), \"os open:\")\n    return perr, ok\n}","tryCatchPattern":"err := driver.Save(ctx, path, artifact)\nif err != nil {\n    var perr *fs.PathError\n    if errors.As(err, &perr) {\n        if errors.Is(perr, fs.ErrNotExist) {\n            log.Printf(\"artifact %s was never produced\", perr.Path)\n        } else if errors.Is(perr, fs.ErrPermission) {\n            log.Printf(\"fix permissions on %s\", perr.Path)\n        }\n    }\n}","preventionTips":["Declare output artifacts only for files the step is guaranteed to write (use mkdir -p and explicit writes).","Run the executor with a user that can read the artifact volume; avoid root-only output files.","Never delete or rename files in the output directory while the workflow is still saving artifacts.","Verify symlinks in artifact dirs resolve to existing targets.","Test artifact paths locally with os.Stat before submitting the workflow."],"tags":["gcs","file-io","os-open","artifact-upload"],"backgroundTag":"file-not-found","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}