moeru-ai/airi · error · Error
`send` doesn't work without stdin input, Claude Code hooks e
Error message
`send` doesn't work without stdin input, Claude Code hooks events are expected to be piped to this command.
What it means
Thrown by the `send` CLI action (cli.ts:60) when process.stdin.isTTY is true, i.e. the command was run in an interactive terminal rather than having a Claude Code hook event piped to its stdin. The send command exists solely to receive the JSON hook payload that Claude Code pipes to configured hook commands, so an interactive invocation has no data to read and is rejected up front before readStdin() blocks forever.
Source
Thrown at plugins/airi-plugin-claude-code/src/cli.ts:60
.action(async (_, flags: Options) => {
if (flags?.quiet) {
logger = logger.withLogLevel(-1 as LogLevel)
}
else {
logger = logger.withLogLevelString(flags?.logLevel ?? LogLevelString.Log)
}
async function readStdin(): Promise<string> {
const chunks: string[] = []
for await (const chunk of stdin) {
chunks.push((chunk as Buffer).toString('utf-8'))
}
return chunks.join('')
}
if (stdin.isTTY) {
throw new Error('`send` doesn\'t work without stdin input, Claude Code hooks events are expected to be piped to this command.')
}
const stdinInput = await readStdin()
if (!stdinInput.trim()) {
throw new Error('`send` received empty stdin input, Claude Code hooks events are expected to be piped to this command.')
}
const hookEvent = JSON.parse(stdinInput) as HookInput
if (hookEvent.hook_event_name === 'UserPromptSubmit') {
const channelServer = new Client({ name: 'proj-airi:plugin-claude-code', autoConnect: false })
await channelServer.connect()
channelServer.send({ type: 'input:text', data: { text: hookEvent.prompt } })
}
})
export async function runCLI(): Promise<void> {View on GitHub (pinned to 27111382b4)
Solutions
- Pipe a Claude Code hook event JSON into the command: `echo '{"hook_event_name":"UserPromptSubmit","prompt":"hi"}' | airi-plugin-claude-code send`.
- In real use, do not invoke `send` by hand — register it as a Claude Code hook command so Claude Code pipes the hook payload to stdin automatically.
- If you need to test the binary in a TTY context, feed it via stdin redirection (`< payload.json`) or `printf '...' |` so stdin.isTTY is false.
- Double-check the Claude Code hook configuration references this binary as the command, not a parent shell that swallows stdin.
Example fix
# before: interactive run, stdin.isTTY === true
$ airi-plugin-claude-code send
# -> `send` doesn't work without stdin input...
# after: pipe the hook payload
$ echo '{"hook_event_name":"UserPromptSubmit","prompt":"hello"}' | airi-plugin-claude-code send
# or: airi-plugin-claude-code send < hook-event.json Defensive patterns
Strategy: validation
Validate before calling
// Guard the CLI action: only attempt readStdin when stdin is actually piped.
if (stdin.isTTY) {
process.stderr.write('error: `send` requires piped hook JSON. Usage: ... | airi-plugin-claude-code send\n')
exit(2)
} Prevention
- Never call `send` interactively — register it as a Claude Code hook command so Claude Code pipes the event JSON.
- When smoke-testing, always pipe a payload (`echo '{...}' | ...`) or redirect a file (`< payload.json`) so stdin.isTTY is false.
- Document the expected invocation in the plugin README so hook wiring always pipes stdin.
When it happens
Trigger: Running `airi-plugin-claude-code send` (or the package's bin) directly from a shell prompt; invoking the binary in a CI step that allocates a TTY but does not pipe a payload; calling the command from a script that forgot to echo/pipe the hook JSON into it.
Common situations: A developer runs the binary manually to smoke-test the hook wiring; the hook entry in Claude Code settings points to the wrong command so Claude Code never pipes anything; a wrapper script launches the binary in a TTY-allocating shell.
Related errors
- `send` received empty stdin input, Claude Code hooks events
- Invalid remote debug port: ${env.APP_REMOTE_DEBUG_PORT}
- No reachable private LAN address is available for the curren
- Failed to apply server channel configuration
- GODOT4 points to a missing Godot executable.\nConfigured pat
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/8fd65dda4fe9db57.
Report an issue: GitHub.