{"record":{"id":"09a685921e34abdf","repo":"multica-ai/multica","slug":"empty-expected-checksum-for-q","errorCode":null,"errorMessage":"empty expected checksum for %q","messagePattern":"empty expected checksum for %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/update.go","lineNumber":217,"sourceCode":"\t\tif len(fields) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tif fields[1] == assetName {\n\t\t\treturn strings.ToLower(fields[0]), nil\n\t\t}\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"read checksum manifest: %w\", err)\n\t}\n\treturn \"\", fmt.Errorf(\"checksum for %q not found in manifest\", assetName)\n}\n\n// verifyAssetSHA256 returns nil when the SHA-256 of data matches the lowercase\n// hex expected value, or an error otherwise. The error includes both digests\n// so a corrupted asset is diagnosable from the log without re-downloading.\nfunc verifyAssetSHA256(data []byte, expectedHex, assetName string) error {\n\tif expectedHex == \"\" {\n\t\treturn fmt.Errorf(\"empty expected checksum for %q\", assetName)\n\t}\n\tsum := sha256.Sum256(data)\n\tactual := hex.EncodeToString(sum[:])\n\tif !strings.EqualFold(actual, expectedHex) {\n\t\treturn fmt.Errorf(\"checksum mismatch for %q: expected %s, got %s\", assetName, expectedHex, actual)\n\t}\n\treturn nil\n}\n\nfunc fetchReleaseByTag(tag string) (*GitHubRelease, error) {\n\tclient := &http.Client{Timeout: 10 * time.Second}\n\treq, err := http.NewRequest(http.MethodGet, \"https://api.github.com/repos/multica-ai/multica/releases/tags/\"+tag, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Accept\", \"application/vnd.github+json\")\n\n\tresp, err := client.Do(req)","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/update.go#L199-L235","documentation":"verifyAssetSHA256 refuses to verify when the expected checksum string is empty — an assertion that the parse step produced a real digest. It is a fail-closed guard: an empty expected value would make a naive comparison trivially bypassable, so it errors naming the asset instead.","triggerScenarios":"A checksums.txt line whose hash field is empty or whitespace (malformed line like '  filename' that still yields two fields after splitting), or a caller passing an unpopulated expected value. Not normally reachable via clean GoReleaser output.","commonSituations":"Hand-edited manifests with the hash accidentally deleted; whitespace-corrupted downloads; future refactors passing a zero-value checksum into the verifier.","solutions":["Inspect checksums.txt for the affected asset's line and restore the full 64-char SHA-256 hex digest","Regenerate the manifest with GoReleaser rather than editing it by hand","Re-download the manifest to rule out truncation/corruption in transit"],"exampleFix":"# before\n     multica-cli-1.2.3-linux-amd64.tar.gz\n\n# after\n3f2a...e1  multica-cli-1.2.3-linux-amd64.tar.gz","handlingStrategy":"validation","validationCode":"if expectedHex == \"\" || len(expectedHex) != 64 {\n\t// manifest line is malformed; refetch or reject before verifying\n\treturn fmt.Errorf(\"bad manifest entry\")\n}","typeGuard":"func isValidSHA256Hex(s string) bool {\n\tif len(s) != 64 { return false }\n\tfor _, r := range s {\n\t\tif !strings.ContainsRune(\"0123456789abcdefABCDEF\", r) { return false }\n\t}\n\treturn true\n}","tryCatchPattern":"if err := cli.VerifyAssetSHA256(data, expected, name); err != nil {\n\tif strings.Contains(err.Error(), \"empty expected checksum\") {\n\t\t// manifest is defective; refuse the update rather than comparing blindly\n\t}\n}","preventionTips":["Validate manifest entries are 64-char hex before trusting them","Generate manifests with GoReleaser instead of editing by hand","Re-download suspicious manifests instead of working around the guard"],"tags":["checksum","security","validation","go"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}