{"record":{"id":"ea74c4d800f998c8","repo":"wezterm/wezterm","slug":"attempted-to-copy-frame-which-is-outside-range","errorCode":null,"errorMessage":"attempted to copy frame {} which is outside range 1-{}","messagePattern":"attempted to copy frame (.+?) which is outside range 1-(.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"term/src/terminalstate/kitty.rs","lineNumber":691,"sourceCode":"                    ),\n                }\n            }\n            ImageDataType::AnimRgba8 {\n                width,\n                height,\n                frames,\n                durations,\n                hashes,\n            } => {\n                let frame_no = frame.frame_number.unwrap_or(frames.len() as u32 + 1);\n                if frame_no == frames.len() as u32 + 1 {\n                    // Append a new frame\n\n                    let mut new_frame = match frame.base_frame {\n                        None => RgbaImage::from_pixel(*width, *height, background_pixel),\n                        Some(n) => {\n                            let n = n as usize;\n                            anyhow::ensure!(\n                                n > 0 && n <= frames.len(),\n                                \"attempted to copy frame {} which is outside range 1-{}\",\n                                n,\n                                frames.len()\n                            );\n                            RgbaImage::from_vec(*width, *height, frames[n - 1].clone()).unwrap()\n                        }\n                    };\n\n                    blit(&mut new_frame, &img, x, y, frame.composition_mode)?;\n\n                    let new_frame_data = new_frame.into_vec();\n                    let new_frame_hash = ImageDataType::hash_bytes(&new_frame_data);\n\n                    frames.push(new_frame_data);\n                    hashes.push(new_frame_hash);\n                    durations.push(frame_gap);\n                } else {","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/wezterm/wezterm/blob/9c04f79f86649f76a8c978ff4b674f60297a6ec9/term/src/terminalstate/kitty.rs#L673-L709","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: 0-based base frame index\nprint(f\"\\x1b_Gf=4,c={idx},a=t,i=5;{payload}\\x1b\\\\\")  # fails when idx == 0 or idx > frame_count\n\n# after: clamp to valid 1-based range\nbase = min(max(idx, 1), frame_count)\nprint(f\"\\x1b_Gf=4,c={base},a=t,i=5;{payload}\\x1b\\\\\")","handlingStrategy":"validation","validationCode":"# base frame (c= key) must be 1..=frame_count when appending\nif base_frame is not None and not (1 <= base_frame <= frame_count):\n    raise ValueError(f\"base frame {base_frame} out of range 1-{frame_count}\")","typeGuard":"def valid_base_frame(base: int | None, frame_count: int) -> bool:\n    return base is None or (1 <= base <= frame_count)","tryCatchPattern":null,"preventionTips":["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"],"tags":["kitty-graphics-protocol","image-animation","frame-validation","off-by-one","terminal"],"backgroundTag":"frame-index-out-of-range","analyzedSha":"9c04f79f86649f76a8c978ff4b674f60297a6ec9","analyzedAt":"2026-08-16T21:23:27.157Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}