{"record":{"id":"acea3baa991bb56c","repo":"henrygd/beszel","slug":"failed-to-open-release-for-checksum-verification","errorCode":null,"errorMessage":"failed to open release for checksum verification: %w","messagePattern":"failed to open release for checksum verification: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ghupdate/checksum.go","lineNumber":29,"sourceCode":")\n\nfunc verifyAssetChecksum(path, digest string) error {\n\talgorithm, expectedHex, ok := strings.Cut(digest, \":\")\n\tif !ok || algorithm == \"\" || expectedHex == \"\" {\n\t\treturn fmt.Errorf(\"invalid release digest %q\", digest)\n\t}\n\tif !strings.EqualFold(algorithm, \"sha256\") {\n\t\treturn fmt.Errorf(\"unsupported release digest algorithm %q\", algorithm)\n\t}\n\n\texpected, err := hex.DecodeString(expectedHex)\n\tif err != nil || len(expected) != sha256.Size {\n\t\treturn fmt.Errorf(\"invalid SHA-256 release digest %q\", digest)\n\t}\n\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open release for checksum verification: %w\", err)\n\t}\n\tdefer file.Close()\n\n\thash := sha256.New()\n\tif _, err := io.Copy(hash, file); err != nil {\n\t\treturn fmt.Errorf(\"failed to calculate release checksum: %w\", err)\n\t}\n\tactual := hash.Sum(nil)\n\tif !bytes.Equal(actual, expected) {\n\t\treturn fmt.Errorf(\"release checksum mismatch: expected %s, got %s\", expectedHex, hex.EncodeToString(actual))\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":11,"sourceCodeEnd":44,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/internal/ghupdate/checksum.go#L11-L44","documentation":"Once the digest parses, verifyAssetChecksum opens the downloaded asset file to hash it. If os.Open fails — the file doesn't exist, permissions deny read, or it's a directory — the error is wrapped with this message. This means the problem is with the local downloaded file, not the digest.","triggerScenarios":"ghupdate.Update -> update -> verifyAssetChecksum when the downloaded asset file at releaseDir/<asset.Name> cannot be opened: download was skipped or cleaned up, disk permissions restrict read access, or antivirus/EDR quarantined the freshly downloaded binary.","commonSituations":"Read-only or noexec-mounted DataDir (e.g. /tmp mounted noexec with odd permission setups); running the updater as a different user than the downloader; antivirus removing downloaded archives before verification; full or failing disk.","solutions":["Check the file exists and is readable at the path referenced in the wrapped error: `ls -la <path>`.","Fix permissions on the DataDir (default os.TempDir()) so the user running the update can read the downloaded asset.","Check for antivirus/EDR quarantine of the downloaded archive and whitelist the update directory.","Retry the update — a transient FS error or cleanup race may have removed the file between download and verify."],"exampleFix":"// before\nsudo -u otheruser ./beszel-agent update   // cannot read files created by root in /tmp\n// after\n./beszel-agent update                      // run as the same user that owns DataDir files","handlingStrategy":"try-catch","validationCode":"if fi, err := os.Stat(cfg.DataDir); err != nil || !fi.IsDir() {\n    return errors.New(\"DataDir must be an existing, accessible directory\")\n}","typeGuard":null,"tryCatchPattern":"updated, err := ghupdate.Update(cfg)\nif err != nil && strings.Contains(err.Error(), \"failed to open release for checksum verification\") {\n    log.Printf(\"downloaded asset unreadable (%v); check permissions/AV and retry\", err)\n    // retry once after confirming the DataDir is writable\n}","preventionTips":["Run the updater as the same user that owns DataDir files.","Whitelist the update directory in antivirus/EDR.","Keep DataDir on a healthy local filesystem, not a flaky network mount."],"tags":["filesystem","file-open","checksum","permissions"],"backgroundTag":"file-open-failed","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}