multica-ai/multica · error
download failed: %w
Error message
download failed: %w
What it means
The release archive itself is fetched with fetchURLBytes; failure is wrapped as 'download failed: %w'. Underlying causes are transport errors (timeout at DefaultUpdateDownloadTimeout of 120s, connection reset, DNS) or the 'HTTP %d from %s' non-200 error. The archive is buffered in memory (~10–30 MB) before any disk write.
Source
Thrown at server/internal/cli/update.go:419
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.
return "", fmt.Errorf("verify download: %w", err)
}
// Extract the binary from the archive.
binaryName := "multica"
if runtime.GOOS == "windows" {
binaryName = "multica.exe"
}
var binaryData []byte
if runtime.GOOS == "windows" {
binaryData, err = extractBinaryFromZip(bytes.NewReader(archiveData), binaryName)View on GitHub (pinned to 2c0912b6ec)
Solutions
- If a custom timeout was passed, raise it or pass 0 to get the 120s default via updateDownloadTimeoutOrDefault.
- Verify the archive URL from the error's wrapped message downloads in a browser/curl.
- Retry — transient CDN resets and half-published releases resolve on the next poll tick.
- Check egress rules for objects.githubusercontent.com in firewalled environments.
Example fix
// before out, err := cli.UpdateViaDownloadWithTimeout(ver, 5*time.Second) // too short for 30MB // after out, err := cli.UpdateViaDownloadWithTimeout(ver, 0) // uses DefaultUpdateDownloadTimeout (120s)
Defensive patterns
Strategy: retry
Try / catch
out, err := cli.UpdateViaDownloadWithTimeout(ver, timeout)
if err != nil {
if strings.HasPrefix(err.Error(), "download failed") {
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
// raise timeout on retry (0 selects the 120s default)
out, err = cli.UpdateViaDownloadWithTimeout(ver, 0)
}
}
} Prevention
- Pass timeout 0 to use the 120s default rather than a short custom value
- Allow egress to objects.githubusercontent.com in firewalled environments
- Retry on the poller's next tick for transient resets
When it happens
Trigger: Slow links exceeding the 120s timeout on a ~30 MB archive; the asset 404ing after the metadata was fetched (release edited mid-update); interrupted connections; a caller passing a short custom downloadTimeout to UpdateViaDownloadWithTimeout.
Common situations: Metered or congested networks; CI environments with restricted egress that allow the API but block the objects CDN (objects.githubusercontent.com); updates attempted while the release assets were being replaced.
Related errors
- update failed: %w
- download checksum manifest: %w
- get attachment: %w
- download file: %w
- install Private Plugin: %w
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/891bd83fe6df2477.
Report an issue: GitHub.