Tencent/WeKnora · error
download failed with status %d
Error message
download failed with status %d
What it means
DownloadFile treats any non-200 response from the attachment host as a failure and records 'download failed with status %d'. 5xx statuses are retried up to maxRetries with exponential backoff (1s, 2s, ...); 4xx and persistent 5xx statuses break out and the error surfaces wrapped as 'download file: download failed with status N'. S3 signed URLs are the usual target, so 403 (expired/invalid signature) and 404 (deleted file) are the dominant cases.
Source
Thrown at internal/datasource/connector/notion/client.go:406
}
var lastErr error
for attempt := 0; attempt <= maxRetries; attempt++ {
resp, err := c.httpClient.Do(req)
if err != nil {
lastErr = err
if attempt < maxRetries {
if sErr := sleepWithContext(ctx, time.Duration(1<<attempt)*time.Second); sErr != nil {
return nil, sErr
}
continue
}
break
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
lastErr = fmt.Errorf("download failed with status %d", resp.StatusCode)
if resp.StatusCode >= 500 && attempt < maxRetries {
if sErr := sleepWithContext(ctx, time.Duration(1<<attempt)*time.Second); sErr != nil {
return nil, sErr
}
continue
}
break
}
data, err := io.ReadAll(io.LimitReader(resp.Body, maxDownloadSize+1))
resp.Body.Close()
if err != nil {
return nil, err
}
if int64(len(data)) > maxDownloadSize {
return nil, fmt.Errorf("file exceeds maximum download size (%d MB)", maxDownloadSize/(1024*1024))
}
return data, nilView on GitHub (pinned to 988cbb0330)
Solutions
- For 403, re-fetch the block via ResolveBlock to obtain a fresh signed URL, then retry the download immediately.
- For 404, check whether the attachment still exists on the page in Notion; remove it from the index or flag the record as stale.
- For 5xx after retries, re-run the sync later — the retry loop already applies exponential backoff, so adding retries in the caller rarely helps.
- Shorten the gap between URL retrieval and download so signed URLs are used within their 1-hour validity window.
Example fix
// before block, _ := client.ResolveBlock(ctx, blockID) // fetched hours earlier, signed URL expired // after block, _ := client.ResolveBlock(ctx, blockID) // re-resolve right before download data, err := client.DownloadFile(ctx, block.File.File.URL) // fresh 1-hour signed URL
Defensive patterns
Strategy: retry
Validate before calling
// signed URLs expire after ~1 hour; check age before downloading
if time.Since(block.FetchedAt) > 45*time.Minute {
block, err = client.ResolveBlock(ctx, block.ID)
if err != nil {
return err
}
} Type guard
func isDownloadStatusError(err error, statuses ...int) bool {
if err == nil {
return false
}
for _, s := range statuses {
if strings.Contains(err.Error(), fmt.Sprintf("download failed with status %d", s)) {
return true
}
}
return false
} Try / catch
data, err := client.DownloadFile(ctx, fileURL)
if err != nil {
if isDownloadStatusError(err, 403) {
// signed URL expired — re-resolve and retry once
if fresh, rErr := client.ResolveBlock(ctx, blockID); rErr == nil {
return client.DownloadFile(ctx, fresh.File.File.URL)
}
}
if isDownloadStatusError(err, 404) {
return nil // attachment deleted; skip
}
return err // 5xx already retried with backoff inside DownloadFile
} Prevention
- Download attachments immediately after resolving blocks — signed URLs expire in about an hour.
- Handle 403 as 're-resolve URL' and 404 as 'attachment gone' rather than generic retries.
- Avoid scheduling downloads of long-stored URL lists; persist block IDs and re-resolve instead.
When it happens
Trigger: The signed S3 URL has expired (Notion file URLs expire after ~1 hour) yielding 403; the attachment was deleted from the page yielding 404; the storage endpoint returns 500/503 beyond the retry budget; a rate-limited 429 from the storage host.
Common situations: Long-running syncs that hold block data for over an hour before downloading; retrying a job hours after fetching the URL list; attachments removed between indexing and download; transient storage outages exceeding maxRetries.
Related errors
- create download request: %w
- download file: %w
- download failed: status=%d
- failed to get file from OBS: %w
- rate limited: %s
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/7e51afbf8de1e887.
Report an issue: GitHub.