{"record":{"id":"4ad3204154fd279e","repo":"netbirdio/netbird","slug":"failed-to-get-oauth-flow-v","errorCode":null,"errorMessage":"failed to get OAuth flow: %v","messagePattern":"failed to get OAuth flow: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/android/login.go","lineNumber":204,"sourceCode":"\t\t}\n\t}\n\n\tgo urlOpener.OnLoginSuccess()\n\n\treturn nil\n}\n\n// loginHintSetter is implemented by both concrete flows (PKCE and device code)\n// but absent from the OAuthFlow interface, hence the assertion below — the same\n// way internal/auth wires it in authenticateWithPKCEFlow.\ntype loginHintSetter interface {\n\tSetLoginHint(hint string)\n}\n\nfunc (a *Auth) foregroundGetTokenInfo(authClient *auth.Auth, urlOpener URLOpener, isAndroidTV bool) (*auth.TokenInfo, error) {\n\toAuthFlow, err := authClient.GetOAuthFlow(a.ctx, isAndroidTV)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get OAuth flow: %v\", err)\n\t}\n\n\t// An empty hint is deliberate, not a fallback: a fresh profile leaves the\n\t// choice to the IdP. Switching accounts is done by switching or removing\n\t// profiles, not by logging out — logout keeps the email.\n\tif a.cfgPath != \"\" {\n\t\tif hint := readProfileEmail(a.cfgPath); hint != \"\" {\n\t\t\tif setter, ok := oAuthFlow.(loginHintSetter); ok {\n\t\t\t\tsetter.SetLoginHint(hint)\n\t\t\t}\n\t\t}\n\t}\n\n\tflowInfo, err := oAuthFlow.RequestAuthInfo(context.TODO())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"getting a request OAuth flow info failed: %v\", err)\n\t}\n","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/android/login.go#L186-L222","documentation":"Thrown by Auth.foregroundGetTokenInfo when auth.Auth.GetOAuthFlow fails to create an OAuth flow on top of the existing management connection. GetOAuthFlow fetches the IdP provider configuration from the management service before any browser login can start, so a broken transport or an unusable IdP setup on the management side surfaces here. The message wraps the underlying error verbatim; the app sees it as 'interactive sso login failed: ...' on the ErrListener, both from Auth.Login and from Client.ExtendAuthSession.","triggerScenarios":"Auth.Login with needsLogin=true, or Client.ExtendAuthSession, calls GetOAuthFlow(ctx, isAndroidTV). It fails when the management connection cannot fetch or decode the OAuth provider info: management unreachable, TLS handshake failure, an HTTP error from the management API, or a malformed/invalid IdP configuration returned by management.","commonSituations":"Wrong or unreachable management URL passed to NewAuth, untrusted self-signed certificate on management, device behind a captive portal, management's OIDC issuer or client ID misconfigured, device clock skew breaking TLS verification.","solutions":["Verify the management URL passed to NewAuth is correct and reachable from the device","Check the management service's IdP/SSO configuration (OIDC issuer, client ID, device flow support)","Read the wrapped error: a transport error points to connectivity, a decode/validation error to management's IdP config","Fix the cause and retry; every attempt builds a fresh flow, no state is poisoned"],"exampleFix":"// before\nauth, _ := android.NewAuth(cfgPath, mgmURL) // mgmURL typo or unreachable\nauth.Login(listener, urlOpener, false)      // later: \"interactive sso login failed: failed to get OAuth flow: ...\"\n\n// after: probe management connectivity and SSO support first\nauth.SaveConfigIfSSOSupported(ssoListener) // only call Login once this reports success and sso==true","handlingStrategy":"try-catch","validationCode":"// Probe management connectivity and SSO support before starting a browser login\nauth.SaveConfigIfSSOSupported(new SSOListener() {\n    public void OnSuccess(boolean sso) { if (sso) auth.Login(errListener, urlOpener, false); }\n    public void OnError(Exception err) { /* surface the config/network problem; do not call Login yet */ }\n});","typeGuard":null,"tryCatchPattern":"// In the ErrListener passed to Login / ExtendAuthSession\nfunc (l *listener) OnError(err error) {\n\tif strings.Contains(err.Error(), \"failed to get OAuth flow\") {\n\t\t// connectivity or management IdP config problem: check URL, network, TLS; safe to retry\n\t}\n}","preventionTips":["Validate the management URL before login; SaveConfigIfSSOSupported doubles as a connectivity probe","Ensure the device trusts management's TLS certificate","Keep management's OIDC configuration valid — a broken IdP config fails every client login","Treat transport failures as retryable; the flow is stateless per attempt"],"tags":["android","go","oauth","network","management"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}