syncthing/syncthing · warning
upload: %s
Error message
upload: %s
What it means
Returned by uploadPanicLog (cmd/syncthing/crash_reporting.go) when the PUT of a panic log to the crash reporting server completed at the HTTP level but returned any status other than 200 OK. The HEAD dedup check (200 = already reported) passed, so this branch means the server actively rejected or failed the upload (e.g. 4xx/5xx, gateway error).
Source
Thrown at cmd/syncthing/crash_reporting.go:116
}
putReq, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(data))
if err != nil {
return err
}
// Set a reasonable timeout on the PUT request
putCtx, putCancel := context.WithTimeout(ctx, putRequestTimeout)
defer putCancel()
putReq = putReq.WithContext(putCtx)
resp, err = http.DefaultClient.Do(putReq)
if err != nil {
return err
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("upload: %s", resp.Status)
}
return nil
}
// filterLogLines returns the data without any log lines between the first
// line and the panic trace. This is done in-place: the original data slice
// is destroyed.
func filterLogLines(data []byte) []byte {
filtered := data[:0]
matched := false
for line := range bytes.SplitSeq(data, []byte("\n")) {
switch {
case !matched && bytes.HasPrefix(line, []byte("Panic ")):
// This begins the panic trace, set the matched flag and append.
matched = true
fallthrough
case len(filtered) == 0 || matched:View on GitHub (pinned to 058bcd7334)
Solutions
- Treat as non-fatal: the panic log stays on disk (not renamed to .reported.log) and is retried on next crash-reporting run — inspect the panic log locally instead
- Check network egress: proxies, TLS interception, or firewall rules rewriting/blocking PUT requests
- If the crash URL is custom-configured, verify the endpoint accepts HEAD+PUT at /<sha256> paths
- Retry later if the crash server was having a transient outage (the log is never lost)
Defensive patterns
Strategy: try-catch
Type guard
func isUploadStatusErr(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "upload: ")
} Try / catch
// Crash reporting is best-effort by design; never let it fail the parent flow
if err := uploadPanicLog(ctx, urlBase, file); err != nil {
if isUploadStatusErr(err) {
slog.WarnContext(ctx, "crash server rejected upload, will retry next run", slogutil.Error(err))
} else {
slog.ErrorContext(ctx, "Reporting crash", slogutil.Error(err))
}
} Prevention
- Treat crash-report upload failures as non-fatal noise; the panic log persists locally until successfully reported
- Verify custom crash URLs accept HEAD and PUT at /<sha256-of-log>
- Keep egress proxies from rewriting PUT bodies or blocking non-GET methods
When it happens
Trigger: Crash reporting enabled (STCRASHURL / crash URL configured) and: the crash server returns 5xx under load, a proxy returns 403/502, the URL base is misconfigured so the PUT path is invalid (404), or the payload is rejected (413). Timeouts are NOT this error (they surface as client.Do errors); only a completed non-OK response triggers it.
Common situations: Self-hosted or proxied crash-reporting endpoints; corporate proxies intercepting the PUT; transient outage of the vendor crash server; typo'd crash-reporting base URL that still serves HTTP.
Related errors
- err.Error()
- resp.Status
- write proxy CONNECT request: %w
- read proxy CONNECT response: %w
- http proxy CONNECT failed: %s
AI-assisted analysis of syncthing/syncthing@058bcd7334 (2026-08-15).
Data as JSON: /api/errors/ca61450cffd9abf9.
Report an issue: GitHub.