{"record":{"id":"ad0d780441aaf667","repo":"argoproj/argo-workflows","slug":"failed-to-open-s-w","errorCode":null,"errorMessage":"failed to open %s: %w","messagePattern":"failed to open (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/argoexec/commands/emissary.go","lineNumber":738,"sourceCode":"\t\treturn fmt.Errorf(\"failed to close %s: %w\", dstPath, err)\n\t}\n\treturn nil\n}\n\nfunc saveParameter(ctx context.Context, template *wfv1.Template, srcPath string) error {\n\tlogger := logging.RequireLoggerFromContext(ctx)\n\n\tif common.FindOverlappingVolume(template, srcPath) != nil {\n\t\tlogger.WithField(\"src\", srcPath).Info(ctx, \"no need to save parameter - on overlapping volume\")\n\t\treturn nil\n\t}\n\tsrc, err := os.Open(filepath.Clean(srcPath))\n\tif os.IsNotExist(err) { // might be optional, so we ignore\n\t\tlogger.WithField(\"src\", srcPath).WithError(err).Warn(ctx, \"cannot save parameter, does not exist\")\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open %s: %w\", srcPath, err)\n\t}\n\tdefer func() { _ = src.Close() }()\n\tdstPath := varRunArgo + \"/outputs/parameters/\" + srcPath\n\tlogger.WithFields(logging.Fields{\n\t\t\"src\": srcPath,\n\t\t\"dst\": dstPath,\n\t}).Info(ctx, \"saving parameter\")\n\tz := filepath.Dir(dstPath)\n\tif mkdirErr := os.MkdirAll(z, 0o755); mkdirErr != nil { // chmod rwxr-xr-x\n\t\treturn fmt.Errorf(\"failed to create directory %s: %w\", z, mkdirErr)\n\t}\n\tdst, err := os.Create(dstPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create %s: %w\", srcPath, err)\n\t}\n\tdefer func() { _ = dst.Close() }()\n\tif _, err = io.Copy(dst, src); err != nil {\n\t\treturn fmt.Errorf(\"failed to copy %s to %s: %w\", srcPath, dstPath, err)","sourceCodeStart":720,"sourceCodeEnd":756,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/cmd/argoexec/commands/emissary.go#L720-L756","documentation":"saveParameter failed to open the source parameter file (the script/based output at srcPath) with an error other than not-exist. Notably, os.IsNotExist is deliberately tolerated (parameters may be optional and a warning is logged instead), so reaching this error means the file exists as a path but couldn't be opened — permission denied, EISDIR, or I/O error. The output parameter cannot be saved.","triggerScenarios":"os.Open(filepath.Clean(srcPath)) returns a non-IsNotExist error: the parameter source path is a directory, the file is unreadable by the argoexec UID, or an I/O error occurs while opening.","commonSituations":"Template's output parameter path points to a directory instead of a file; script writes output as root but argoexec runs as non-root without read permission; SELinux blocking read; typo making path land on a device/special file.","solutions":["Check the wrapped error: EISDIR means the parameter path must point to a regular file.","chmod/chown the output file so the executor user can read it (e.g. chmod 644 in the script).","If the output may legitimately be absent, keep it optional — missing files already return nil with a warning.","Verify the template's output.parameters[].valueFrom.path is correct and points to a file."],"exampleFix":"// before: script writes as root with 600, argoexec reads as non-root\nscript:\n  source: echo -n hi > /tmp/out; chmod 600 /tmp/out\n// after: make it readable\nscript:\n  source: echo -n hi > /tmp/out; chmod 644 /tmp/out","handlingStrategy":"validation","validationCode":"// validate parameter source path before it matters\np := tmpl.Outputs.Parameters[0].ValueFrom.Path\nfi, err := os.Stat(p)\nif err != nil {\n    return fmt.Errorf(\"parameter path %s missing\", p) // mark optional if acceptable\n}\nif fi.IsDir() {\n    return fmt.Errorf(\"parameter path %s is a directory, must be a file\", p)\n}\nif f, err := os.Open(p); err != nil {\n    return fmt.Errorf(\"parameter path %s unreadable: %w\", p, err)\n} else {\n    f.Close()\n}","typeGuard":null,"tryCatchPattern":"err := runStep(ctx)\nif err != nil && strings.Contains(err.Error(), \"failed to open\") {\n    unwrapped := errors.Unwrap(err)\n    if errors.Is(unwrapped, syscall.EISDIR) {\n        return fixParameterPathToFile(ctx)\n    }\n    if errors.Is(unwrapped, os.ErrPermission) {\n        return fixFilePermissionsAndRetry(ctx)\n    }\n    return err\n}","preventionTips":["Point valueFrom.path at a regular file, never a directory","chmod 644 output files written by other UIDs in the pod","Treat possibly-absent outputs as optional parameters (missing files are tolerated)","Lint templates so path typos are caught before runtime"],"tags":["argo-workflows","argoexec","parameters","filesystem"],"backgroundTag":"file-open-failed","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}