syncthing/syncthing · error
handling %v: %w
Error message
handling %v: %w
What it means
Wraps an error that occurred while processing (not decoding) an already-parsed BEP message, such as applying an index or dispatching a Request to a handler. The %v names the message context and %w the handler failure. It is produced by newHandleError at the dispatch point in the message loop (protocol.go:509), distinguishing downstream handler failures from wire-format errors.
Source
Thrown at lib/protocol/protocol.go:1119
return nil, fmt.Errorf("decompressed message len %d is too large", size)
}
buf := BufferPool.Get(int(size))
n, err := lz4.UncompressBlock(src[4:], buf)
if err != nil {
BufferPool.Put(buf)
return nil, err
}
return buf[:n], nil
}
func newProtocolError(err error, msgContext string) error {
return fmt.Errorf("protocol error on %v: %w", msgContext, err)
}
func newHandleError(err error, msgContext string) error {
return fmt.Errorf("handling %v: %w", msgContext, err)
}
func messageContext(msg proto.Message) (string, error) {
switch msg := msg.(type) {
case *bep.ClusterConfig:
return "cluster-config", nil
case *bep.Index:
return fmt.Sprintf("index for %v", msg.Folder), nil
case *bep.IndexUpdate:
return fmt.Sprintf("index-update for %v", msg.Folder), nil
case *bep.Request:
return fmt.Sprintf(`request for "%v" in %v`, msg.Name, msg.Folder), nil
case *bep.Response:
return "response", nil
case *bep.DownloadProgress:
return fmt.Sprintf("download-progress for %v", msg.Folder), nil
case *bep.Ping:
return "ping", nilView on GitHub (pinned to 058bcd7334)
Solutions
- Check which message context appears in the error text (e.g. 'index for X') and verify that folder is shared and configured on both devices
- Inspect the wrapped %w error — it names the real handler failure (disk, database, unknown folder)
- Sync folder configs across devices and restart the connection so a fresh ClusterConfig is exchanged
- If persistent, rescan or reset the folder index database on the affected device
Example fix
// before: handler error surfaces opaquely
case errors.As(err, &e):
log.Printf("sync failed: %v", err)
// after: unwrap to distinguish handle vs protocol errors via error text
if strings.HasPrefix(err.Error(), "handling ") {
log.Printf("handler failure on message: %v", errors.Unwrap(err))
} Defensive patterns
Strategy: try-catch
Validate before calling
// before exchanging indexes, confirm the folder is actually shared both ways
for _, folder := range myFolders {
if !peerSharesFolder(peerID, folder.ID) {
return fmt.Errorf("folder %s not shared with peer", folder.ID)
}
} Type guard
func isHandleError(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "handling ")
} Try / catch
if err := msgLoop(); err != nil {
if isHandleError(err) {
// inspect errors.Unwrap(err) for the real cause; may be per-folder and non-fatal
l.Warnf("message handler failed: %v", errors.Unwrap(err))
}
} Prevention
- Apply folder config changes on all devices before reconnecting
- Keep folders shared symmetrically between peers
- Monitor wrapped causes: unknown-folder and disk errors point to config/storage, not protocol
When it happens
Trigger: The messageLoop dispatches a decoded message to its handler (index processing, request handling, cluster-config processing) and the handler returns a non-nil error; e.g. an index for an unknown folder or a request for a file that cannot be opened.
Common situations: Folder removed from config on one side while the other still sends index updates; disk full or file locked when serving a Request; database errors in the folder while applying an index; partial config propagation in a cluster.
Related errors
- protocol error: %w
- closed by remote: %v
- invalid state %d
- request size %d too small
- protocol error on %v: %w
AI-assisted analysis of syncthing/syncthing@058bcd7334 (2026-08-15).
Data as JSON: /api/errors/8b8391d078290d91.
Report an issue: GitHub.