{"record":{"id":"e90099072e2535da","repo":"nektos/act","slug":"unable-to-os-open-v","errorCode":null,"errorMessage":"Unable to os.Open: %v","messagePattern":"Unable to os\\.Open: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/exprparser/functions.go","lineNumber":229,"sourceCode":"\t\tif fi.IsDir() || !matcher.Match(parts, fi.IsDir()) {\n\t\t\treturn nil\n\t\t}\n\t\tfiles = append(files, path)\n\t\treturn nil\n\t}); err != nil {\n\t\treturn \"\", fmt.Errorf(\"Unable to filepath.Walk: %v\", err)\n\t}\n\n\tif len(files) == 0 {\n\t\treturn \"\", nil\n\t}\n\n\thasher := sha256.New()\n\n\tfor _, file := range files {\n\t\tf, err := os.Open(file)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"Unable to os.Open: %v\", err)\n\t\t}\n\n\t\tif _, err := io.Copy(hasher, f); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"Unable to io.Copy: %v\", err)\n\t\t}\n\n\t\tif err := f.Close(); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"Unable to Close file: %v\", err)\n\t\t}\n\t}\n\n\treturn hex.EncodeToString(hasher.Sum(nil)), nil\n}\n\nfunc (impl *interperterImpl) getNeedsTransitive(job *model.Job) []string {\n\tneeds := job.Needs()\n\n\tfor _, need := range needs {","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/exprparser/functions.go#L211-L247","documentation":"Thrown by hashFiles() when streaming a matched file into the SHA-256 hasher fails (io.Copy error). Usually the read is interrupted: file truncated/removed mid-read, an I/O error on disk, or the file is a fifo that yields an error.","triggerScenarios":"Concurrent writers truncating files while hashFiles evaluates; failing disks or NFS stale handles; matching named pipes created by dev servers.","commonSituations":"hashFiles computed while a watcher/dev process rewrites files; hashFiles inside a loop with a build step mutating outputs; container mounts with flaky I/O.","solutions":["Evaluate hashFiles before steps that mutate the hashed files (cache keys at step start).","Exclude volatile directories from the pattern.","Stabilize the source tree before hashing (stop dev servers)."],"exampleFix":"# before (hash while building)\n- run: npm run build\n- uses: actions/cache@v4\n  with:\n    key: ${{ hashFiles('dist/**') }}\n# after (hash sources only)\n- uses: actions/cache@v4\n  with:\n    key: ${{ hashFiles('src/**', 'package-lock.json') }}\n- run: npm run build","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var h string\nfor attempt := 0; attempt < 2; attempt++ {\n  var err error\n  h, err = hashFiles('src/**')\n  if err == nil || !strings.Contains(err.Error(), 'io.Copy') { break }\n  time.Sleep(100 * time.Millisecond) // file churned during read\n}","preventionTips":["Compute hashFiles before mutating steps run","Exclude build output dirs from patterns","Stop dev servers/watchers before evaluation"],"tags":["expressions","hash-files","io","race-condition"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}