{"record":{"id":"4dd38bdead17e7d2","repo":"nektos/act","slug":"broken-file-v-v","errorCode":null,"errorMessage":"broken file: %v != %v","messagePattern":"broken file: (.+?) != (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"pkg/artifactcache/storage.go","lineNumber":89,"sourceCode":"\t\tf, err := os.Open(v)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tn, err := io.Copy(file, f)\n\t\t_ = f.Close()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\twritten += n\n\t}\n\n\t// If size is less than 0, it means the size is unknown.\n\t// We can't check the size of the file, just skip the check.\n\t// It happens when the request comes from old versions of actions, like `actions/cache@v2`.\n\tif size >= 0 && written != size {\n\t\t_ = file.Close()\n\t\t_ = os.Remove(name)\n\t\treturn 0, fmt.Errorf(\"broken file: %v != %v\", written, size)\n\t}\n\n\treturn written, nil\n}\n\nfunc (s *Storage) Serve(w http.ResponseWriter, r *http.Request, id uint64) {\n\tname := s.filename(id)\n\thttp.ServeFile(w, r, name)\n}\n\nfunc (s *Storage) Remove(id uint64) {\n\t_ = os.Remove(s.filename(id))\n\t_ = os.RemoveAll(s.tempDir(id))\n}\n\nfunc (s *Storage) filename(id uint64) string {\n\treturn filepath.Join(s.rootDir, fmt.Sprintf(\"%02x\", id%0xff), fmt.Sprint(id))\n}","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/artifactcache/storage.go#L71-L107","documentation":"Storage.Commit concatenates the uploaded temp chunk files into the final cache file and compares total bytes written against the size the client declared when finalizing the cache entry. A mismatch means the stored chunks do not add up to the declared size, so act removes the incomplete file and fails the commit — the cache upload is treated as broken/interrupted. Size < 0 (unknown size, actions/cache@v2) skips the check.","triggerScenarios":"Finalizing a cache upload (POST /caches) where chunks were lost, duplicated, or partially written before commit — e.g. a PATCH chunk failed silently, a previous partial commit left stale temp files, or the declared size was computed before the content changed.","commonSituations":"Network hiccup or client abort during chunk upload on the local loopback proxy; two uploads racing for the same cache id; act killed during upload leaving stale temp chunks that get mixed into a later commit; flaky disk.","solutions":["Simply rerun the workflow — the failed commit is cleaned up and a fresh upload usually succeeds","Clear the cache server temp dir (~/.cache/act/actcache*/_tmp/ inside the cache dir) if stale chunks persist","Ensure only one run uploads a given cache key at a time","If persistent, verify disk health and free space on the cache volume"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"written, err := storage.Commit(id, size)\nif err != nil && strings.HasPrefix(err.Error(), \"broken file\") {\n    // upload integrity broke; discard and retry the whole upload\n    storage.Remove(id)\n    return retryUpload()\n}\nreturn written, err","preventionTips":["Retry failed cache saves once before giving up","Clear stale temp chunk dirs between runs","Don't run two uploads for the same cache id concurrently"],"tags":["artifact-cache","upload","storage","data-integrity","act"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}