wezterm/wezterm · error · anyhow::Error
attempted to copy frame {} which is outside range 1-{}
Error message
attempted to copy frame {} which is outside range 1-{} What it means
When appending a new frame to an existing animated image (ImageDataType::AnimRgba8), the kitty protocol lets the new frame copy its base from an earlier frame (base_frame, the 'c' key in the escape sequence). WezTerm validates that base_frame is within 1..=frames.len(); otherwise it bails with this message before compositing. It fires only on the append path (frame_number is len+1 or None), where a base frame is requested with an out-of-range index.
Source
Thrown at term/src/terminalstate/kitty.rs:691
),
}
}
ImageDataType::AnimRgba8 {
width,
height,
frames,
durations,
hashes,
} => {
let frame_no = frame.frame_number.unwrap_or(frames.len() as u32 + 1);
if frame_no == frames.len() as u32 + 1 {
// Append a new frame
let mut new_frame = match frame.base_frame {
None => RgbaImage::from_pixel(*width, *height, background_pixel),
Some(n) => {
let n = n as usize;
anyhow::ensure!(
n > 0 && n <= frames.len(),
"attempted to copy frame {} which is outside range 1-{}",
n,
frames.len()
);
RgbaImage::from_vec(*width, *height, frames[n - 1].clone()).unwrap()
}
};
blit(&mut new_frame, &img, x, y, frame.composition_mode)?;
let new_frame_data = new_frame.into_vec();
let new_frame_hash = ImageDataType::hash_bytes(&new_frame_data);
frames.push(new_frame_data);
hashes.push(new_frame_hash);
durations.push(frame_gap);
} else {View on GitHub (pinned to 9c04f79f86)
Solutions
- Use 1-based frame numbers for the c= (base frame) key: valid values are 1 through the current frame count
- Query the image (a=q,i=<id>) or locally track the frame count and clamp base_frame to that range before transmitting
- If the animation state is uncertain, retransmit the full animation from frame 1 under a fresh image id
Example fix
# before: 0-based base frame index
print(f"\x1b_Gf=4,c={idx},a=t,i=5;{payload}\x1b\\") # fails when idx == 0 or idx > frame_count
# after: clamp to valid 1-based range
base = min(max(idx, 1), frame_count)
print(f"\x1b_Gf=4,c={base},a=t,i=5;{payload}\x1b\\") Defensive patterns
Strategy: validation
Validate before calling
# base frame (c= key) must be 1..=frame_count when appending
if base_frame is not None and not (1 <= base_frame <= frame_count):
raise ValueError(f"base frame {base_frame} out of range 1-{frame_count}") Type guard
def valid_base_frame(base: int | None, frame_count: int) -> bool:
return base is None or (1 <= base <= frame_count) Prevention
- Treat all kitty frame numbers as 1-based
- Track frame counts per image id locally instead of deriving them from loop indices
- Re-query the image state when resuming an interrupted animation
When it happens
Trigger: Sending `<ESC>_Gc=N,f=F,a=t,...` where N is 0 or greater than the current frame count of image id, while F addresses a new frame (frame_number omitted or equal to current frame count + 1). Zero-indexed c=0 is the classic trigger since kitty frame numbers are 1-based.
Common situations: Senders that use 0-based frame indices while the kitty spec is 1-based; stale animation state where the image was replaced by a shorter one under the same id; scripts that hard-code a base frame after trimming frames elsewhere.
Related errors
- attempted to edit frame {} but there is only a single frame
- attempted to edit frame {} which is outside range 1-{}
- refusing to display image: terminal has no cell pixel dimens
- refusing to display image with zero draw dimensions (WxH: {}
- Ignoring image data for image with dimensions {}x{} because
AI-assisted analysis of wezterm/wezterm@9c04f79f86 (2026-08-16).
Data as JSON: /api/errors/ea74c4d800f998c8.
Report an issue: GitHub.