kovidgoyal/kitty · error · TransmissionError
EISDIR
EISDIR
Error message
Cannot write data to a directory entry
What it means
DestFile.write_data refuses to append payload bytes to an entry whose ftype is FileType.directory, because directories have no file content. It raises TransmissionError with code EISDIR, mirroring the OS errno.
Source
Thrown at kitty/file_transmission.py:515
else:
os.chmod(self.name, self.permissions)
if self.mtime != FileTransmissionCommand.mtime:
if is_symlink:
with suppress(NotImplementedError):
os.utime(self.name, ns=(self.mtime, self.mtime), follow_symlinks=False)
else:
os.utime(self.name, ns=(self.mtime, self.mtime))
def unlink_existing_if_needed(self, force: bool = False) -> None:
if force or self.needs_unlink:
with suppress(FileNotFoundError):
os.unlink(self.name)
self.existing_stat = None
self.needs_unlink = False
def write_data(self, all_files: dict[str, 'DestFile'], data: bytes | memoryview, is_last: bool) -> None:
if self.ftype is FileType.directory:
raise TransmissionError(code=ErrorCode.EISDIR, file_id=self.file_id, msg='Cannot write data to a directory entry')
if self.closed:
raise TransmissionError(file_id=self.file_id, msg='Cannot write to a closed file')
if self.ftype in (FileType.symlink, FileType.link):
self.link_target += data
self.bytes_written += len(data)
if is_last:
lt = self.link_target.decode('utf-8', 'replace')
base = self.make_parent_dirs()
self.unlink_existing_if_needed(force=True)
if lt.startswith('fid:'):
lt = all_files[lt[4:]].name
if self.ftype is FileType.symlink:
lt = os.path.relpath(lt, os.path.dirname(self.name))
elif lt.startswith('fid_abs:'):
lt = all_files[lt[8:]].name
elif lt.startswith('path:'):
lt = lt[5:]
if not os.path.isabs(lt) and self.ftype is FileType.link:View on GitHub (pinned to 6d5d0c4406)
Solutions
- Fix the sender to skip data actions for directory entries (only send file/end_data for regular files).
- If hand-writing the protocol, ensure action=data is only used after a file-type start with content.
Example fix
# before
if spec.is_dir:
pass # still falls through to send data
# after
if spec.is_dir:
continue # no data frames for directories Defensive patterns
Strategy: validation
Validate before calling
if entry.ftype is FileType.directory:
return # skip data phase for dirs Type guard
def accepts_data(ftype) -> bool:
return ftype is not FileType.directory Try / catch
try:
add_data(ftc)
except TransmissionError as e:
if e.code is ErrorCode.EISDIR: log.warning('ignoring data for directory %s', e.file_id)
else: raise Prevention
- Senders: never emit data frames for directory specs.
- Test directory transfers explicitly.
When it happens
Trigger: A peer sends an 'action=data' (or end_data) command for a file_id previously started with a directory entry — e.g. buggy sender that always streams data even for dir entries.
Common situations: Directory-transfer senders that emit an empty data frame for every spec including directories; version mismatch between sender/receiver implementations; hand-crafted test sequences.
Related errors
- EINVAL
- Not an appropriate file type
- No valid action specified in file transmission command
- Cannot write to a closed file
- Unknown link target type
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/51ff3ed0b3944ca2.
Report an issue: GitHub.