{"record":{"id":"ed4ecb6e17f20418","repo":"ruvnet/ruflo","slug":"login-cancelled-no-code-was-entered","errorCode":null,"errorMessage":"login cancelled: no code was entered","messagePattern":"login cancelled: no code was entered","errorType":"exception","errorClass":"LoginCancelledError","httpStatus":null,"severity":"warning","filePath":"v3/@claude-flow/cli/src/auth/client.ts","lineNumber":165,"sourceCode":"\n  const pkce = sec.generatePkce();\n  const url = sec.authorizeUrl(sec.OOB_REDIRECT_URI, pkce.state, pkce.codeChallenge);\n  print(`Open this URL in a browser and authorize:\\n\\n  ${url}\\n`);\n\n  // `rl.question()` resolves on a newline-terminated 'line' event — if `input`\n  // ends without ever emitting one (e.g. stdin closed early, or piped input\n  // with no trailing newline), it hangs forever rather than treating EOF as\n  // a cancellation. Race it against the interface's own 'close' event so an\n  // early EOF resolves to \"\" (-> LoginCancelledError below) instead of hanging.\n  const rl = readline.createInterface({ input, terminal: false });\n  let code: string;\n  try {\n    const closed = new Promise<string>((resolve) => rl.once('close', () => resolve('')));\n    code = (await Promise.race([rl.question('Paste the code shown after authorizing: '), closed])).trim();\n  } finally {\n    rl.close();\n  }\n  if (!code) throw new LoginCancelledError();\n\n  const tokens = await sec.exchangeManualCode(code, pkce.codeVerifier);\n  return { tokens, method: 'device' };\n}\n\n/**\n * `--token-stdin`: reads one JSON object from stdin,\n * `{access_token, refresh_token?, expires_in, scope}`. Wire format is not\n * specified by ADR-306 — defined here as typed JSON rather than a bare\n * token string, so scope/expiry are explicit rather than inferred.\n */\nexport async function tokenStdinLogin(input: NodeJS.ReadableStream = process.stdin): Promise<LoginResult> {\n  const chunks: Buffer[] = [];\n  for await (const chunk of input) chunks.push(chunk as Buffer);\n  const raw = Buffer.concat(chunks).toString('utf-8').trim();\n  if (!raw) throw new Error('--token-stdin: no input received on stdin');\n\n  let parsed: { access_token?: string; refresh_token?: string; expires_in?: number; scope?: string };","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/auth/client.ts#L147-L183","documentation":"Thrown by the manual/headless login path (LoginCancelledError) when the user enters an empty code or closes stdin before entering one. The readline race against the 'close' event converts EOF to an empty string, which is treated as an explicit cancellation rather than a hang.","triggerScenarios":"Calling manualLogin and the user presses Enter at the 'Paste the code' prompt with nothing typed, or stdin reaches EOF (piped input ended without a code).","commonSituations":"User aborted; piped input was empty or malformed; non-interactive shell with no tty and no input.","solutions":["Re-run the login and paste the full code from the authorize page.","In scripts, ensure stdin is a pipe that actually contains the code with a trailing newline."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const code = (await readLine()).trim();\nif (code.length === 0) {\n  console.error('No code entered. Re-run and paste the code from the authorize page.');\n  process.exit(1);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await manualLogin(print, input);\n} catch (e) {\n  if (e instanceof Error && /no code was entered|login cancelled/.test(e.message)) {\n    // user-initiated cancel; exit 0 or re-prompt\n  } else throw e;\n}","preventionTips":["In scripts, verify stdin is a pipe with content before invoking manual login.","Distinguish cancellation (exit 0) from real errors.","Surface a clear re-run command in the message."],"tags":["oauth","manual-login","user-cancellation","auth"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}