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      = 8

View on GitHub (pinned to 058bcd7334)

Solutions

  1. No action needed if syncing works: this is normal connection de-duplication, treat it as informational.
  2. If connections flap, check NAT/relay churn that causes constant re-dialing rather than this error itself.
  3. 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

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


AI-assisted analysis of syncthing/syncthing@058bcd7334 (2026-08-15). Data as JSON: /api/errors/643e2ba01663c58e. Report an issue: GitHub.