{"record":{"id":"0cce12b3f7b073a7","repo":"netbirdio/netbird","slug":"failed-to-parse-redirect-url-v","errorCode":null,"errorMessage":"failed to parse redirect URL: %v","messagePattern":"failed to parse redirect URL: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/internal/auth/pkce_flow.go","lineNumber":198,"sourceCode":"\n// WaitToken waits for the OAuth token in the PKCE Authorization Flow.\n// It starts an HTTP server to receive the OAuth token callback and waits for the token or an error.\n// Once the token is received, it is converted to TokenInfo and validated before returning.\n// The method creates a timeout context internally based on info.ExpiresIn.\nfunc (p *PKCEAuthorizationFlow) WaitToken(ctx context.Context, info AuthFlowInfo) (TokenInfo, error) {\n\t// Create timeout context based on flow expiration\n\ttimeout := time.Duration(info.ExpiresIn) * time.Second\n\twaitCtx, cancel := context.WithTimeout(ctx, timeout)\n\tdefer cancel()\n\n\tlog.Infof(\"pkce flow: waiting for authorization callback on %s, timeout %s\", p.oAuthConfig.RedirectURL, timeout)\n\n\ttokenChan := make(chan *oauth2.Token, 1)\n\terrChan := make(chan error, 1)\n\n\tparsedURL, err := url.Parse(p.oAuthConfig.RedirectURL)\n\tif err != nil {\n\t\treturn TokenInfo{}, fmt.Errorf(\"failed to parse redirect URL: %v\", err)\n\t}\n\n\tserver := &http.Server{Addr: fmt.Sprintf(\":%s\", parsedURL.Port())}\n\tdefer func() {\n\t\tshutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\n\t\tif err := server.Shutdown(shutdownCtx); err != nil {\n\t\t\tlog.Errorf(\"failed to close the server: %v\", err)\n\t\t}\n\t}()\n\n\tgo p.startServer(server, tokenChan, errChan)\n\n\tselect {\n\tcase <-waitCtx.Done():\n\t\treturn TokenInfo{}, waitCtx.Err()\n\tcase token := <-tokenChan:","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/auth/pkce_flow.go#L180-L216","documentation":"Returned by PKCEAuthorizationFlow.WaitToken when url.Parse rejects the selected redirect URL (client/internal/auth/pkce_flow.go:196-199). The parsed URL's port is needed to bind the local callback server (http.Server on ':'+port). url.Parse fails only on truly malformed input - ASCII control characters or invalid percent-encoding - so in practice this means the redirect URL stored in the management/IdP configuration is malformed. Note the URL already passed isRedirectURLPortUsed during flow construction, which also parses and would have logged a parse failure there.","triggerScenarios":"p.oAuthConfig.RedirectURL contains control characters (e.g. a stray newline or NUL pasted into the admin config) or an invalid escape like '%zz'. url.Parse errors where a hostname would not: most typo'd URLs (missing scheme, bad port text) still parse, only control chars and broken percent-escapes do not.","commonSituations":"Copy-paste artifacts in the IdP integration config on management (trailing newline, hidden whitespace/control char), or programmatic config writes that injected unescaped characters.","solutions":["Administrator: re-enter the redirect URL in the management/IdP configuration cleanly (e.g. http://localhost:53000/callback), avoiding pasted control characters","Verify with a quick parse before deploying config changes (see validationCode)","End user: retry after the corrected config is fetched from management"],"exampleFix":"// before: config with a stray control character\nRedirectURLs: []string{\"http://localhost:53000/callback\\n\"}\n\n// after: clean URL\nRedirectURLs: []string{\"http://localhost:53000/callback\"}","handlingStrategy":"validation","validationCode":"// Administrator-side: validate redirect URLs before saving them into the IdP integration\nfunc validRedirectURL(raw string) error {\n\tu, err := url.Parse(raw)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"redirect URL %q is not parseable: %w\", raw, err)\n\t}\n\tif u.Port() == \"\" {\n\t\treturn fmt.Errorf(\"redirect URL %q must include an explicit port\", raw)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"failed to parse redirect URL\") {\n\t// malformed URL in management/IdP config: strip control characters (e.g. trailing newline) and re-save\n}","preventionTips":["Type redirect URLs manually or sanitize pasted values (no trailing newlines, spaces, or control characters)","Validate URLs in config automation with url.Parse before publishing","Remember url.Parse only rejects control characters and bad percent-escapes - most typos parse fine but break the flow later"],"tags":["pkce","redirect-url","url-parsing","configuration","validation"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}