kovidgoyal/kitty · error · StreamError
No stream_id in rc payload
Error message
No stream_id in rc payload
What it means
StreamError from kitty's remote-control streaming layer when a streamed data payload lacks a valid 'stream_id' string. stream_id correlates chunks with the in-flight stream started by a prior ESC @ stream-control sequence; without it the data cannot be routed and the command is rejected.
Source
Thrown at kitty/rc/base.py:440
windows += list(tab)
return windows
def create_async_responder(self, payload_get: PayloadGetType, window: Window | None) -> AsyncResponder:
return AsyncResponder(payload_get, window)
def message_to_kitty(self, global_opts: RCOptions, opts: Any, args: ArgsType) -> PayloadType:
raise NotImplementedError()
def response_from_kitty(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> ResponseType:
raise NotImplementedError()
def cancel_async_request(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> None:
pass
def handle_streamed_data(self, data: bytes, payload_get: PayloadGetType) -> BytesIO | AsyncResponse:
stream_id = payload_get('stream_id')
if not stream_id or not isinstance(stream_id, str):
raise StreamError('No stream_id in rc payload')
return self.stream_in_flight.handle_data(stream_id, data)
def cli_params_for(command: RemoteCommand) -> tuple[Callable[[], str], str, str, str]:
return (command.options_spec or '\n').format, command.args.spec, command.desc, f'kitten @ {command.name}'
def parse_subcommand_cli(command: RemoteCommand, args: ArgsType) -> tuple[Any, ArgsType]:
opts, items = parse_args(args[1:], *cli_params_for(command), result_class=command.options_class)
if command.args.args_count is not None and command.args.args_count != len(items):
if command.args.args_count == 0:
raise SystemExit(f'Unknown extra argument(s) supplied to {command.name}')
raise SystemExit(f'Must specify exactly {command.args.args_count} argument(s) for {command.name}')
return opts, items
def display_subcommand_help(func: RemoteCommand) -> None:
with suppress(SystemExit):View on GitHub (pinned to 6d5d0c4406)
Solutions
- When starting a stream, capture the stream_id from the initial response and include it in every subsequent data payload
- Verify the payload key is exactly 'stream_id' and is a non-empty string
- Prefer using 'kitten @ ...' CLI or the kittens RPC helpers rather than hand-rolling the streaming protocol
Defensive patterns
Strategy: validation
Validate before calling
sid = payload.get('stream_id')
if not isinstance(sid, str) or not sid:
raise ValueError('cannot stream without a stream_id from the initial stream-open response')
send_stream_chunk(sid, data) Type guard
def has_stream_id(payload: dict) -> bool:
sid = payload.get('stream_id')
return isinstance(sid, str) and len(sid) > 0 Try / catch
try:
conn.handle_streamed_data(data, payload_get)
except StreamError as e:
if 'No stream_id' in str(e):
reopen_stream() # re-negotiate stream and resend with fresh id
else:
raise Prevention
- Always store the stream_id from the stream-open response and attach it to every chunk
- Use kitty's own client helpers instead of hand-crafting streaming escape sequences
When it happens
Trigger: Calling handle_streamed_data with a payload whose stream_id is missing, empty, or not a string — e.g. hand-crafted escape sequences for remote control streaming that skip the stream_id field, or a client library that drops the key during JSON serialization.
Common situations: Writing a custom remote-control client, protocol-level scripts emitting kitty escape codes, or version mismatches where the client omits the newer stream_id field.
Related errors
- Unsupported version received in edit protocol: {self.version
- Too much data being sent
- This should be run as kitten icat
- This should be run as `kitten notify ...`
- Remote control not enabled, this kitten should be run via a
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/f43a9249dea3f41c.
Report an issue: GitHub.