syncthing/syncthing · info · errDeviceAlreadyConnected
already connected to this device
Error message
already connected to this device
What it means
errDeviceAlreadyConnected is a connection-rejection sentinel meaning the service already has a connection to that device ID, so the new candidate connection is refused instead of duplicating it. Syncthing keeps (at most) one preferred connection per device; when several candidates arrive (both sides dialing, multiple addresses), losers are rejected with this error and the dial loop treats it as success-equivalent, not a failure.
Source
Thrown at lib/connections/service.go:69
var (
dialers = make(map[string]dialerFactory)
listeners = make(map[string]listenerFactory)
)
var (
// Dialers and listeners return errUnsupported (or a wrapped variant)
// when they are intentionally out of service due to configuration,
// build, etc. This is not logged loudly.
errUnsupported = errors.New("unsupported protocol")
// These are specific explanations for errUnsupported.
errDisabled = fmt.Errorf("%w: disabled by configuration", errUnsupported)
errDeprecated = fmt.Errorf("%w: deprecated", errUnsupported)
// Various reasons to reject a connection
errNetworkNotAllowed = errors.New("network not allowed")
errDeviceAlreadyConnected = errors.New("already connected to this device")
errDeviceIgnored = errors.New("device is ignored")
errConnLimitReached = errors.New("connection limit reached")
errDevicePaused = errors.New("device is paused")
// A connection is being closed to make space for better ones
errReplacingConnection = errors.New("replacing connection")
)
const (
perDeviceWarningIntv = 15 * time.Minute
tlsHandshakeTimeout = 10 * time.Second
minConnectionLoopSleep = 5 * time.Second
stdConnectionLoopSleep = time.Minute
worstDialerPriority = math.MaxInt32
recentlySeenCutoff = 7 * 24 * time.Hour
shortLivedConnectionThreshold = 5 * time.Second
dialMaxParallel = 64
dialMaxParallelPerDevice = 8View on GitHub (pinned to 058bcd7334)
Solutions
- No action needed if syncing works: this is normal connection de-duplication, treat it as informational.
- If connections flap, check NAT/relay churn that causes constant re-dialing rather than this error itself.
- When writing tooling around the connection service, handle this sentinel explicitly so it is not reported as a failure.
Defensive patterns
Strategy: fallback
Try / catch
if err := dialDevice(id); err != nil {
if errors.Is(err, errDeviceAlreadyConnected) {
// success-equivalent: a connection already exists; reuse it
return existingConn(id)
}
} Prevention
- Check for an existing connection before initiating a new one when embedding Syncthing.
- Treat this sentinel as benign in monitoring and dashboards.
- Do not configure alerting on it; alert on zero-connections instead.
When it happens
Trigger: Two devices dial each other simultaneously, or a device answers while an outbound dial to it is in flight; the second candidate hits the already-connected check in the connection service and is rejected with errDeviceAlreadyConnected.
Common situations: Symmetric setups where both configs list each other (the normal case) causing connection races; log noise showing the error even though connectivity is fine.
Related errors
- network not allowed
- connection limit reached
- replacing connection
- connected to self
- no multicast interfaces available
AI-assisted analysis of syncthing/syncthing@058bcd7334 (2026-08-15).
Data as JSON: /api/errors/643e2ba01663c58e.
Report an issue: GitHub.