{"record":{"id":"ccd9c2c0415a0f5b","repo":"sipeed/picoclaw","slug":"manual-input-canceled","errorCode":null,"errorMessage":"manual input canceled","messagePattern":"manual input canceled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/auth/oauth.go","lineNumber":168,"sourceCode":"\tdefer close(manualDone)\n\tgo func() {\n\t\treader := bufio.NewReader(browserLoginInput)\n\t\tinput, _ := reader.ReadString('\\n')\n\t\tselect {\n\t\tcase manualCh <- strings.TrimSpace(input):\n\t\tcase <-manualDone:\n\t\t}\n\t}()\n\n\tselect {\n\tcase result := <-resultCh:\n\t\tif result.err != nil {\n\t\t\treturn nil, result.err\n\t\t}\n\t\treturn ExchangeCodeForTokens(cfg, result.code, pkce.CodeVerifier, redirectURI)\n\tcase manualInput := <-manualCh:\n\t\tif manualInput == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"manual input canceled\")\n\t\t}\n\t\t// Extract code from URL if it's a full URL\n\t\tcode := manualInput\n\t\tif strings.Contains(manualInput, \"?\") {\n\t\t\tu, err := url.Parse(manualInput)\n\t\t\tif err == nil {\n\t\t\t\tcode = u.Query().Get(\"code\")\n\t\t\t}\n\t\t}\n\t\tif code == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"could not find authorization code in input\")\n\t\t}\n\t\treturn ExchangeCodeForTokens(cfg, code, pkce.CodeVerifier, redirectURI)\n\tcase <-time.After(5 * time.Minute):\n\t\treturn nil, fmt.Errorf(\"authentication timed out after 5 minutes\")\n\t}\n}\n","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/auth/oauth.go#L150-L186","documentation":"The manual-input fallback path (oauth.go:168) received an empty string: the user dismissed or submitted nothing at the paste-the-redirect-URL prompt while the browser callback never arrived. It is a user-abort signal, not a system fault — the login simply has no code to exchange.","triggerScenarios":"User hits Enter on an empty prompt or cancels the terminal read (EOF) in the NoBrowser/manual flow before the browser redirect lands anywhere.","commonSituations":"Headless/SSH sessions using the manual flow; user realizes the browser never opened and aborts; paste of whitespace-only input.","solutions":["Re-run the login command and complete it","Paste the full localhost callback URL (it contains ?code=...) instead of submitting empty","If the browser cannot open (no GUI), make sure to copy the printed auth URL into a browser on another machine and bring back the redirect URL","Use the device-code flow (RequestDeviceCode) when paste-back is awkward"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// at the CLI layer around the manual prompt\ninput := strings.TrimSpace(readLine())\nif input == \"\" {\n    fmt.Println(\"no input given — aborting login; re-run and paste the redirect URL\")\n    return errAbort\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"manual input canceled\") {\n    // user aborted: guide, don't crash\n    fmt.Println(\"login canceled — run the login command again and paste the localhost callback URL\")\n    return nil\n}","preventionTips":["Tell users up front to paste the full localhost/auth/callback URL","Offer the device-code flow when manual paste is error-prone","Trim whitespace from pasted input before treating it as canceled"],"tags":["oauth","user-input","ux","login"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}