{"record":{"id":"31a33713a7237882","repo":"argoproj/argo-workflows","slug":"test-if-s-is-a-dir-w","errorCode":null,"errorMessage":"test if %s is a dir: %w","messagePattern":"test if (.+?) is a dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/gcs/gcs.go","lineNumber":283,"sourceCode":"\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tfs, err := listFileRelPaths(path+file.Name()+string(os.PathSeparator), relPath+file.Name()+string(os.PathSeparator))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresults = append(results, fs...)\n\t\t} else {\n\t\t\tresults = append(results, relPath+file.Name())\n\t\t}\n\t}\n\treturn results, nil\n}\n\n// upload a local file or dir to GCS\nfunc uploadObjects(ctx context.Context, client *storage.Client, bucket, key, path string) error {\n\tisDir, err := file.IsDirectory(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"test if %s is a dir: %w\", path, err)\n\t}\n\tif isDir {\n\t\tdirName := filepath.Clean(path) + string(os.PathSeparator)\n\t\tkeyPrefix := filepath.Clean(key) + \"/\"\n\t\tfileRelPaths, listErr := listFileRelPaths(dirName, \"\")\n\t\tif listErr != nil {\n\t\t\treturn listErr\n\t\t}\n\t\tfor _, relPath := range fileRelPaths {\n\t\t\tfullKey := normalizeGCSKey(keyPrefix + relPath)\n\t\t\terr = uploadObject(ctx, client, bucket, fullKey, dirName+relPath)\n\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)","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/gcs/gcs.go#L265-L301","documentation":"uploadObjects uploads a local path to GCS; first it checks with file.IsDirectory(path) whether the source is a directory. This error wraps a failure of that check itself. It throws when the driver cannot even stat/open the local source path, not when the upload fails.","triggerScenarios":"file.IsDirectory returns an error for the artifact source path during artifact Save: the path does not exist, permission denied, or the path is on a broken mount — i.e. os.Stat/open failed.","commonSituations":"Artifact path never produced by the step (script wrote elsewhere or failed silently); volume not mounted at the expected path; output parameter/artifact path typo in the template.","solutions":["Verify the template actually creates the file/directory at the artifact's path","Check the artifact path spelling and that the volume is mounted there","Add an `ls -la`/debug step or use argo logs to inspect the container filesystem","Check permissions of the path for the container user"],"exampleFix":"// before\nartifacts: [{name: result, path: /out/result.json}]  # script writes ./result.json\n// after\ncontainer:\n  command: [sh, -c, 'mkdir -p /out && cp result.json /out/']\nartifacts: [{name: result, path: /out/result.json}]","handlingStrategy":"validation","validationCode":"// inside the producing step, before it exits:\nimport \"os\"\nfunc assertPathExists(p string) {\n\tif _, err := os.Stat(p); err != nil {\n\t\tpanic(fmt.Sprintf(\"artifact source missing: %v\", err))\n\t}\n}\n// assertPathExists(\"/out/result.json\") at end of script","typeGuard":null,"tryCatchPattern":"if err := saveArtifacts(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"test if \") {\n\t\t// local source missing: check the producing step's outputs before retrying\n\t}\n\treturn err\n}","preventionTips":["Use explicit absolute artifact paths and create them with mkdir -p in the step","Validate templates with `argo lint` so path/volume mismatches are caught early","Verify volumeMounts cover every artifact path"],"tags":["filesystem","gcs","artifacts","upload"],"backgroundTag":"artifact-source-path-missing","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"}