{"record":{"id":"c99c976c0fef0ea5","repo":"apache/beam","slug":"failed-to-scan-v-for-artifacts-to-stage","errorCode":null,"errorMessage":"failed to scan %v for artifacts to stage","messagePattern":"failed to scan (.+?) for artifacts to stage","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/artifact/stage.go","lineNumber":217,"sourceCode":"\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn hex.EncodeToString(sha256W.Sum(nil)), nil\n}\n\n// KeyedFile is a key and filename pair.\ntype KeyedFile struct {\n\tKey, Filename string\n}\n\nfunc scan(dir string) ([]KeyedFile, error) {\n\tvar ret []KeyedFile\n\tif err := walk(dir, \"\", &ret); err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to scan %v for artifacts to stage\", dir)\n\t}\n\treturn ret, nil\n}\n\nfunc walk(dir, key string, accum *[]KeyedFile) error {\n\tlist, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, elm := range list {\n\t\tk := makeKey(key, elm.Name())\n\t\tf := filepath.Join(dir, elm.Name())\n\n\t\tif elm.IsDir() {\n\t\t\twalk(f, k, accum)\n\t\t\tcontinue\n\t\t}","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/artifact/stage.go#L199-L235","documentation":"scan() walks the staging source directory recursively collecting KeyedFile entries; it wraps any error from walk() (which delegates to os.ReadDir) as 'failed to scan %v for artifacts to stage'. This means the source directory could not be read at all — StageDir aborts before staging anything. Note that StageDir silently returns no artifacts (and nil error) if the directory is readable but empty.","triggerScenarios":"os.ReadDir(dir) inside walk fails because the directory does not exist, is not a directory, or lacks read permission; os.ReadDir on a subdirectory errors mid-walk (note: subdirectory walk errors are ignored, only the top-level error propagates).","commonSituations":"Passing a wrong --artifact staging dir / working-dir path to the pipeline runner; the directory was deleted or renamed before staging; running the pipeline as a user without read permission on the directory; passing a file path instead of a directory.","solutions":["Verify the directory path exists and is a directory: os.Stat + Mode().IsDir() before calling StageDir.","Check read/execute permissions on the directory (and traverse permissions on parents) for the user running the pipeline.","Correct the source path passed to StageDir — it must be the directory containing artifacts, not a file.","Ensure the directory isn't removed by earlier pipeline steps or cleanup jobs before staging runs."],"exampleFix":"// before\nmds, err := artifact.StageDir(ctx, client, srcDir, token)\n// after (validate first)\nif info, statErr := os.Stat(srcDir); statErr != nil || !info.IsDir() {\n\treturn fmt.Errorf(\"staging source %q is not a readable directory\", srcDir)\n}\nmds, err := artifact.StageDir(ctx, client, srcDir, token)","handlingStrategy":"validation","validationCode":"func validateStagingDir(dir string) error {\n\tinfo, err := os.Stat(dir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"staging dir %q: %w\", dir, err)\n\t}\n\tif !info.IsDir() {\n\t\treturn fmt.Errorf(\"%q is not a directory\", dir)\n\t}\n\tf, err := os.Open(dir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"no read permission on %q: %w\", dir, err)\n\t}\n\tf.Close()\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"list, err := artifact.StageDir(ctx, client, srcDir, token)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to scan\") {\n\t\treturn fmt.Errorf(\"check that %s exists, is a directory, and is readable: %w\", srcDir, err)\n\t}\n\treturn err\n}\nif len(list) == 0 {\n\treturn fmt.Errorf(\"staging dir %s contained no artifacts\", srcDir)\n}","preventionTips":["os.Stat and confirm IsDir before calling StageDir.","Check directory read/execute permissions for the pipeline user.","Beware: an empty but readable directory yields no error and no artifacts — assert non-empty results.","Confirm no cleanup job deletes the directory between pipeline steps and staging."],"tags":["filesystem","directory","scan","artifact-staging","go","beam"],"backgroundTag":"directory-not-found","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}