kovidgoyal/kitty · error · TransmissionError

Cannot write to a file without first starting it

Error message

Cannot write to a file without first starting it

What it means

add_data looks up the DestFile by ftc.file_id; if no prior action=file/start created it, there is nothing to write to and it raises TransmissionError('Cannot write to a file without first starting it').

Source

Thrown at kitty/file_transmission.py:630

    def cancel(self) -> None:
        self.close()

    def start_file(self, ftc: FileTransmissionCommand) -> DestFile:
        self.last_activity_at = monotonic()
        if ftc.file_id in self.files:
            raise TransmissionError(
                msg=f'The file_id {ftc.file_id} already exists',
                file_id=ftc.file_id,
            )
        self.files[ftc.file_id] = df = DestFile(ftc)
        return df

    def add_data(self, ftc: FileTransmissionCommand) -> DestFile:
        self.last_activity_at = monotonic()
        df = self.files.get(ftc.file_id)
        if df is None:
            raise TransmissionError(file_id=ftc.file_id, msg='Cannot write to a file without first starting it')
        if df.failed:
            return df
        try:
            df.write_data(self.files, ftc.data, ftc.action is Action.end_data)
        except Exception:
            df.failed = True
            with suppress(Exception):
                df.close()
            raise
        return df

    def commit(self, send_os_error: Callable[[OSError, str, 'ActiveReceive', str], None]) -> None:
        directories = sorted((df for df in self.files.values() if df.ftype is FileType.directory), key=lambda x: len(x.name), reverse=True)
        for df in directories:
            with suppress(OSError):
                # we ignore failures to apply directory metadata as we have already sent an OK for the dir
                df.apply_metadata()

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Always begin each file with action=file (start) before any data frames.
  2. If transfers may be interrupted, restart the whole transfer rather than resuming mid-stream.
  3. Test the pipeline with kitty alone (no tmux/screen) to rule out sequence loss.
Defensive patterns

Strategy: validation

Validate before calling

assert ftc.file_id in receiver.files, f'not started: {ftc.file_id}'

Try / catch

try:
    receiver.add_data(ftc)
except TransmissionError as e:
    if 'without first starting' in e.msg: restart_transfer()
    else: raise

Prevention

When it happens

Trigger: Sending action=data for a file_id never started in this session, or after the receiver was reset/cleared — commonly a lost or dropped start frame.

Common situations: Escape-sequence start frame swallowed by a pager/multiplexer; sender state reset mid-transfer; hand-crafted sequences skipping the start step.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/cc4395458626a215. Report an issue: GitHub.