netbirdio/netbird · warning

session extend already in progress

Error message

session extend already in progress

What it means

The client allows exactly one ExtendAuthSession in flight: beginExtend refuses to create a second context while extendCancel is non-nil, because the underlying PKCE flow binds a fixed loopback port that a concurrent flow could not acquire. The error is reported to the second call's ErrListener; the first flow is unaffected.

Source

Thrown at client/android/session.go:264

		if err != nil {
			log.Warnf("session warning event with unparsable deadline: %v", err)
			continue
		}
		lead, err := sessionwatch.ParseLeadMinutes(meta[sessionwatch.MetaSessionLeadMinutes])
		if err != nil {
			// Informational only — the deadline above is what drives the UI.
			lead = 0
		}
		listener.OnSessionExpiring(deadline.Unix(), int64(lead),
			meta[sessionwatch.MetaSessionFinal] == "true")
	}
}

func (c *Client) beginExtend() (context.Context, error) {
	c.extendMu.Lock()
	defer c.extendMu.Unlock()
	if c.extendCancel != nil {
		return nil, fmt.Errorf("session extend already in progress")
	}
	ctx, cancel := context.WithCancel(context.Background())
	c.extendCancel = cancel
	return ctx, nil
}

func (c *Client) endExtend() {
	c.extendMu.Lock()
	defer c.extendMu.Unlock()
	if c.extendCancel != nil {
		c.extendCancel()
		c.extendCancel = nil
	}
}

func (c *Client) extendAuthSession(ctx context.Context, urlOpener URLOpener, isAndroidTV bool) error {
	cfg, cfgPath, cc := c.authSnapshot()
	if cfg == nil || cc == nil {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Wait for the first flow's OnSuccess/OnError before starting another
  2. If the user abandoned the browser, call CancelExtendAuthSession first, then start the new flow
  3. Disable the renew action in the UI while a flow is in flight

Example fix

// before
renewBtn.setOnClickListener { client.ExtendAuthSession(opener, false, listener) } // double-tap → second call errors

// after: cancel the abandoned flow before re-starting
renewBtn.setOnClickListener {
	if (extendInFlight) client.CancelExtendAuthSession()
	extendInFlight = true
	client.ExtendAuthSession(opener, false, listener)
}
Defensive patterns

Strategy: validation

Validate before calling

// Track flow state in the UI and cancel before re-starting
if (extendInFlight) client.CancelExtendAuthSession()
extendInFlight = true
client.ExtendAuthSession(opener, isTV, listener)

Type guard

fun canStartExtend(inFlight: Boolean): Boolean = !inFlight // gate the button on this

Try / catch

// In the ErrListener of ExtendAuthSession
func (l *extendListener) OnError(err error) {
	if strings.Contains(err.Error(), "session extend already in progress") {
		// benign: another flow is running; either wait for its result or cancel it first
		return
	}
	l.show(err)
}

Prevention

When it happens

Trigger: Calling ExtendAuthSession again while a previous one is still waiting for the user to complete the browser step — a double-tap on the renew button, or a retry after the user abandoned the browser without calling CancelExtendAuthSession (which keeps the loopback port bound for the full flow timeout).

Common situations: UI that does not disable the renew button while a flow is in flight; user backs out of the browser and immediately retries; stale tap events queued during the browser round-trip.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/20d1b8ceedc9db94. Report an issue: GitHub.