ruvnet/ruflo · error
--token-stdin: no input received on stdin
Error message
--token-stdin: no input received on stdin
What it means
tokenStdinLogin read stdin to EOF and got nothing but whitespace. The --token-stdin path expects exactly one JSON object ({access_token, refresh_token?, expires_in, scope}) on stdin; an empty stream means the caller invoked the flag without piping anything — typically a human running the command interactively, or a pipeline whose upstream produced no output.
Solutions
- Verify the upstream producer: run `cat | whatever-emits-token` in isolation and confirm it prints the JSON object
- Pipe explicitly, e.g. `cat token.json | ruflo auth login --token-stdin` or `ruflo auth login --token-stdin < token.json`
- If the token comes from a secret store, fail that fetch loudly before invoking the CLI so empty output can't reach stdin
- If you meant an interactive login, drop --token-stdin and use the default browser or manual flow
Example fix
# before — flag with nothing piped
ruflo auth login --token-stdin
# after — explicit, fail-fast token source
token=$(get_token) || exit 1
[ -n "$token" ] || { echo 'token source empty'; exit 1; }
printf '%s' "$token" | ruflo auth login --token-stdin Defensive patterns
Strategy: validation
Validate before calling
// shell: refuse to invoke on empty input
[ -s token.json ] || { echo 'token file empty'; exit 1; }
ruflo auth login --token-stdin < token.json
// node: check the stream yields bytes before calling tokenStdinLogin
const chunks = [];
for await (const c of input) chunks.push(c);
if (chunks.length === 0) throw new Error('no token on stdin — aborting before tokenStdinLogin'); Try / catch
try { await tokenStdinLogin(process.stdin); }
catch (e) {
if (e instanceof Error && e.message.includes('no input received on stdin')) {
// fix the producer: nothing was piped; don't retry unchanged
}
throw e;
} Prevention
- Always redirect from a file or pipe: --token-stdin is never interactive
- Fail loudly in the secret-fetch step so empty tokens can't reach the CLI
- Size-check the token file with [ -s ] before invoking
When it happens
Trigger: Running `ruflo auth login --token-stdin` in an interactive shell (no pipe attached — stdin is the TTY and may be closed/Ctrl-D'd), or `some-cmd | ruflo auth login --token-stdin` where some-cmd printed nothing (failed silently, empty secret).
Common situations: Scripts assuming a credential helper emits JSON when it actually failed quietly; interactive users exploring flags; CI steps where the secret-injection step was skipped or referenced an empty variable; Windows shells dropping pipe content.
Related errors
- --token-stdin expects a single JSON object
- --token-stdin: JSON is missing required field "access_token"
- login cancelled: no code was entered
- not logged in for profile
- authorization was denied or failed
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/c19871fcf5fdf8dd.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/auth/client.ts:181
rl.close();
}
if (!code) throw new LoginCancelledError();
const tokens = await sec.exchangeManualCode(code, pkce.codeVerifier);
return { tokens, method: 'device' };
}
/**
* `--token-stdin`: reads one JSON object from stdin,
* `{access_token, refresh_token?, expires_in, scope}`. Wire format is not
* specified by ADR-306 — defined here as typed JSON rather than a bare
* token string, so scope/expiry are explicit rather than inferred.
*/
export async function tokenStdinLogin(input: NodeJS.ReadableStream = process.stdin): Promise<LoginResult> {
const chunks: Buffer[] = [];
for await (const chunk of input) chunks.push(chunk as Buffer);
const raw = Buffer.concat(chunks).toString('utf-8').trim();
if (!raw) throw new Error('--token-stdin: no input received on stdin');
let parsed: { access_token?: string; refresh_token?: string; expires_in?: number; scope?: string };
try {
parsed = JSON.parse(raw);
} catch {
throw new Error(
'--token-stdin expects a single JSON object: {"access_token","refresh_token"?,"expires_in","scope"}',
);
}
if (!parsed.access_token) throw new Error('--token-stdin: JSON is missing required field "access_token"');
const tokens: OAuthTokenResponse = {
access_token: parsed.access_token,
token_type: 'Bearer',
refresh_token: parsed.refresh_token,
expires_in: parsed.expires_in,
};
return { tokens, method: 'token-stdin' };View on GitHub (pinned to fa13ee4ad6)