{"record":{"id":"20d1b8ceedc9db94","repo":"netbirdio/netbird","slug":"session-extend-already-in-progress","errorCode":null,"errorMessage":"session extend already in progress","messagePattern":"session extend already in progress","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/android/session.go","lineNumber":264,"sourceCode":"\t\tif err != nil {\n\t\t\tlog.Warnf(\"session warning event with unparsable deadline: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tlead, err := sessionwatch.ParseLeadMinutes(meta[sessionwatch.MetaSessionLeadMinutes])\n\t\tif err != nil {\n\t\t\t// Informational only — the deadline above is what drives the UI.\n\t\t\tlead = 0\n\t\t}\n\t\tlistener.OnSessionExpiring(deadline.Unix(), int64(lead),\n\t\t\tmeta[sessionwatch.MetaSessionFinal] == \"true\")\n\t}\n}\n\nfunc (c *Client) beginExtend() (context.Context, error) {\n\tc.extendMu.Lock()\n\tdefer c.extendMu.Unlock()\n\tif c.extendCancel != nil {\n\t\treturn nil, fmt.Errorf(\"session extend already in progress\")\n\t}\n\tctx, cancel := context.WithCancel(context.Background())\n\tc.extendCancel = cancel\n\treturn ctx, nil\n}\n\nfunc (c *Client) endExtend() {\n\tc.extendMu.Lock()\n\tdefer c.extendMu.Unlock()\n\tif c.extendCancel != nil {\n\t\tc.extendCancel()\n\t\tc.extendCancel = nil\n\t}\n}\n\nfunc (c *Client) extendAuthSession(ctx context.Context, urlOpener URLOpener, isAndroidTV bool) error {\n\tcfg, cfgPath, cc := c.authSnapshot()\n\tif cfg == nil || cc == nil {","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/android/session.go#L246-L282","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Wait for the first flow's OnSuccess/OnError before starting another","If the user abandoned the browser, call CancelExtendAuthSession first, then start the new flow","Disable the renew action in the UI while a flow is in flight"],"exampleFix":"// before\nrenewBtn.setOnClickListener { client.ExtendAuthSession(opener, false, listener) } // double-tap → second call errors\n\n// after: cancel the abandoned flow before re-starting\nrenewBtn.setOnClickListener {\n\tif (extendInFlight) client.CancelExtendAuthSession()\n\textendInFlight = true\n\tclient.ExtendAuthSession(opener, false, listener)\n}","handlingStrategy":"validation","validationCode":"// Track flow state in the UI and cancel before re-starting\nif (extendInFlight) client.CancelExtendAuthSession()\nextendInFlight = true\nclient.ExtendAuthSession(opener, isTV, listener)","typeGuard":"fun canStartExtend(inFlight: Boolean): Boolean = !inFlight // gate the button on this","tryCatchPattern":"// In the ErrListener of ExtendAuthSession\nfunc (l *extendListener) OnError(err error) {\n\tif strings.Contains(err.Error(), \"session extend already in progress\") {\n\t\t// benign: another flow is running; either wait for its result or cancel it first\n\t\treturn\n\t}\n\tl.show(err)\n}","preventionTips":["Disable the renew button while a flow is in flight; re-enable on OnSuccess/OnError","Call CancelExtendAuthSession whenever the user abandons the browser — the PKCE port stays bound otherwise","Model the extend flow as single-instance in the UI, mirroring the client's own guard"],"tags":["android","go","session","concurrency","oauth"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}