multica-ai/multica · error

parse checksum manifest: %w

Error message

parse checksum manifest: %w

What it means

parseChecksumManifest parses the GoReleaser checksums.txt looking for the line whose filename matches the release asset; failure is wrapped as 'parse checksum manifest: %w'. It fails when the manifest has no entry for the asset or the line format is not '<hex> <filename>'.

Source

Thrown at server/internal/cli/update.go:408

	}
	manifestAsset, err := findChecksumManifestAsset(release.Assets)
	if err != nil {
		return "", err
	}
	downloadURL := asset.BrowserDownloadURL
	assetName := asset.Name

	// Pull the checksum manifest first so a release that is half-published
	// (archives uploaded but checksums.txt not yet) fails before we eat the
	// archive's bandwidth.
	timeout := updateDownloadTimeoutOrDefault(downloadTimeout)
	manifestData, err := fetchURLBytes(manifestAsset.BrowserDownloadURL, timeout)
	if err != nil {
		return "", fmt.Errorf("download checksum manifest: %w", err)
	}
	expectedSum, err := parseChecksumManifest(manifestData, assetName)
	if err != nil {
		return "", fmt.Errorf("parse checksum manifest: %w", err)
	}

	// Buffer the archive into memory so we can verify the full SHA-256
	// before writing anything to disk. Release archives are ~10–30 MB; the
	// extraction code already buffers zip archives in full (random access
	// requirement), so this is not a new memory cost on Windows. For tar.gz
	// it adds a single in-RAM copy, which is preferable to running the
	// untrusted bytes through gzip+tar extraction before the SHA-256 check.
	archiveData, err := fetchURLBytes(downloadURL, timeout)
	if err != nil {
		return "", fmt.Errorf("download failed: %w", err)
	}

	if err := verifyAssetSHA256(archiveData, expectedSum, assetName); err != nil {
		// Do NOT extract or replace; the next poll tick will retry. A
		// corrupted asset is rare enough that retrying through the same
		// CDN is the right default; persistent failures will surface in
		// the daemon log.

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Download checksums.txt from the release and confirm an exact line for the asset name (compare byte-for-byte, including .tar.gz vs .zip).
  2. Align findReleaseAsset's candidate names with what GoReleaser actually publishes (check .goreleaser.yml archives.name_template).
  3. If using a custom release process, emit checksums in the standard '<sha256> <filename>' format with two spaces.
  4. Re-release with a corrected manifest if the file itself is wrong.

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

// before updating, confirm the manifest has an entry for the asset you expect
manifest, _ := fetchURLBytes(manifestURL, timeout)
if _, err := parseChecksumManifest(manifest, expectedAssetName); err != nil {
    // release packaging mismatch: stop before downloading the archive
}

Try / catch

out, err := cli.UpdateViaDownload(ver)
if err != nil && strings.HasPrefix(err.Error(), "parse checksum manifest") {
    // asset naming drift: compare .goreleaser.yml name_template with the updater's candidates
}

Prevention

When it happens

Trigger: Asset names in the release changed (new GOOS/GOARCH naming, archive suffix change) so the manifest entry no longer matches what findReleaseAsset picked; checksums.txt hand-edited or generated by a different tool with another format; a manifest listing CRLF-separated lines a strict parser misses.

Common situations: A release pipeline change (GoReleaser config rename) applied to the archive name but not to the code's asset-candidate list, causing a name mismatch; building custom release artifacts with a non-GoReleaser checksum layout.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/e97d4769f4bf2c62. Report an issue: GitHub.