{"record":{"id":"65fe7a7078ea5430","repo":"ruvnet/ruflo","slug":"token-stdin-expects-a-single-json-object-acce","errorCode":null,"errorMessage":"--token-stdin expects a single JSON object: {\"access_token\",\"refresh_token\"?,\"expires_in\",\"scope\"}","messagePattern":"--token-stdin expects a single JSON object: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/auth/client.ts","lineNumber":187,"sourceCode":"}\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 };\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    throw new Error(\n      '--token-stdin expects a single JSON object: {\"access_token\",\"refresh_token\"?,\"expires_in\",\"scope\"}',\n    );\n  }\n  if (!parsed.access_token) throw new Error('--token-stdin: JSON is missing required field \"access_token\"');\n\n  const tokens: OAuthTokenResponse = {\n    access_token: parsed.access_token,\n    token_type: 'Bearer',\n    refresh_token: parsed.refresh_token,\n    expires_in: parsed.expires_in,\n  };\n  return { tokens, method: 'token-stdin' };\n}\n\n/**\n * Refreshes an access token. Classifies failure into network-unreachable\n * vs. a reachable-but-erroring server so callers can print an honest\n * message instead of collapsing both into \"offline\" (ADR-308 failure","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/auth/client.ts#L169-L205","documentation":"tokenStdinLogin got non-empty stdin but JSON.parse failed: the input is not a single valid JSON object. The expected wire format (defined in client.ts, not by ADR-306) is one object {\"access_token\": string, \"refresh_token\"?: string, \"expires_in\": number, \"scope\": string} — not a bare token string, not a JWT, not shell-quoted JSON, not multiple JSON documents.","triggerScenarios":"`ruflo auth login --token-stdin` fed a raw access-token string or JWT (both invalid JSON), JSON wrapped in single quotes by a shell heredoc, pretty-printed JSON split across documents, or output with log lines mixed around the JSON (e.g. 'Fetching... {\"access_token\"...}').","commonSituations":"Piping `echo $ACCESS_TOKEN` instead of a JSON envelope; credential helpers emitting extra prose; YAML/ini-style config pasted instead of JSON; trailing commas or comments (invalid in JSON); UTF-8 BOM from Windows tooling.","solutions":["Wrap the token exactly as specified: printf '{\"access_token\":\"%s\",\"expires_in\":3600,\"scope\":\"...\"}' \"$TOK\" | ruflo auth login --token-stdin","Strip non-JSON noise: pipe through a jq stage — jq -n --arg t \"$TOK\" '{access_token:$t,expires_in:3600,scope:\"api\"}' — so the bytes are guaranteed valid JSON","Validate locally first: the same bytes must pass jq . or node -e 'JSON.parse(require(\"fs\").readFileSync(0))'","Remember the required field: even valid JSON without access_token throws the sibling 'missing required field' error — include it"],"exampleFix":"# before — bare token string (not JSON)\necho \"$ACCESS_TOKEN\" | ruflo auth login --token-stdin\n\n# after — single JSON object on stdin\nprintf '{\"access_token\":\"%s\",\"expires_in\":3600,\"scope\":\"api\"}' \"$ACCESS_TOKEN\" \\\n  | ruflo auth login --token-stdin","handlingStrategy":"validation","validationCode":"// validate shape before the CLI sees it\nconst obj = JSON.parse(raw);\nif (typeof obj?.access_token !== 'string' || typeof obj?.expires_in !== 'number')\n  throw new Error('token JSON malformed — rebuild the envelope');\n// or generate it: jq -n --arg t \"$TOK\" '{access_token:$t,expires_in:3600,scope:\"api\"}'","typeGuard":"function isTokenEnvelope(v: unknown): v is { access_token: string; refresh_token?: string; expires_in?: number; scope?: string } {\n  return typeof v === 'object' && v !== null &&\n    typeof (v as any).access_token === 'string';\n}","tryCatchPattern":"try { await tokenStdinLogin(input); }\ncatch (e) {\n  if (e instanceof Error && e.message.includes('expects a single JSON object')) {\n    // input wasn't JSON: pipe through `jq .` or rebuild the envelope and retry\n  }\n  throw e;\n}","preventionTips":["Pipe the token through jq so output is guaranteed-valid JSON","Use the documented envelope; a bare JWT string is rejected","Keep log noise out of the token stream — separate transport from logging"],"tags":["oauth","auth","json-parse","stdin","token"],"backgroundTag":"invalid-json-parse","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}