{"record":{"id":"d606b992b51c283c","repo":"sgl-project/sglang","slug":"memory-video-len-must-be-a-multiple-of-latent-heig","errorCode":null,"errorMessage":"memory_video_len must be a multiple of latent_height * latent_width, got {memory_video_len=} {latent_height=} {latent_width=}","messagePattern":"memory_video_len must be a multiple of latent_height \\* latent_width, got (.+?) (.+?) (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/joy_echo/memory.py","lineNumber":446,"sourceCode":"    fps: float,\n    memory_position_mode: str,\n    memory_downscale_factor: int = 1,\n    sp_target_start_offset: int = 0,\n) -> torch.Tensor:\n    \"\"\"Build [memory | target] video RoPE coordinates.\n\n    Under sequence parallelism the target video latents are time-sharded, so\n    ``target_num_frames`` is the *local* shard frame count and\n    ``sp_target_start_offset`` is the global frame index of this rank's first\n    target frame. The memory prefix is replicated (full) on every rank.\n    \"\"\"\n    tokens_per_latent_frame = int(latent_height) * int(latent_width)\n    if tokens_per_latent_frame <= 0:\n        raise ValueError(\n            f\"Invalid latent grid for memory RoPE: {latent_height=} {latent_width=}\"\n        )\n    if memory_video_len % tokens_per_latent_frame != 0:\n        raise ValueError(\n            \"memory_video_len must be a multiple of latent_height * latent_width, \"\n            f\"got {memory_video_len=} {latent_height=} {latent_width=}\"\n        )\n\n    memory_latent_frames = memory_video_len // tokens_per_latent_frame\n    position_mode = normalize_memory_position_mode(memory_position_mode)\n\n    memory_coords = rope.prepare_video_coords(\n        batch_size=batch_size,\n        num_frames=memory_latent_frames,\n        height=latent_height,\n        width=latent_width,\n        device=device,\n        fps=JOYAI_VIDEO_ROPE_FPS,\n        start_frame=0,\n    )\n    memory_coords = apply_memory_video_downscale(memory_coords, memory_downscale_factor)\n","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/joy_echo/memory.py#L428-L464","documentation":"The flattened memory video token count must be an exact multiple of latent_height * latent_width, i.e. tokens must decompose into whole latent frames. If not, the RoPE builder cannot determine the number of memory latent frames, which usually means the latent grid or the token count is inconsistent with the VAE layout.","triggerScenarios":"Calling build_memory_video_rope_coords where memory_video_len (total memory video tokens) was produced with a different latent_height/latent_width than the ones passed — e.g. tokens computed from a 12x20 grid but coordinates built with 8x8, or a truncated/dropped-token memory sequence.","commonSituations":"Mismatched VAE downsample config between tokenization and RoPE-coordinate stages; sequence trimming/chunking that cuts the memory prefix at a non-frame-aligned boundary; TP sharding that splits tokens without respecting frame boundaries.","solutions":["Recompute latent_height/latent_width from the same VAE config used to tokenize the memory video, and pass those exact values","Ensure any trimming/merging of the memory token sequence keeps its length a multiple of tokens_per_latent_frame (align cuts to frame boundaries)","Print/inspect memory_video_len % (latent_height*latent_width) at runtime to find which stage corrupts the invariant"],"exampleFix":"# before\ncoords = build_memory_video_rope_coords(memory_video_len=1920, latent_height=8, latent_width=8)\n# after\ntplf = latent_height * latent_width\nassert memory_video_len % tplf == 0, f\"misaligned memory len {memory_video_len} vs grid {latent_height}x{latent_width}\"\ncoords = build_memory_video_rope_coords(memory_video_len=memory_video_len, latent_height=latent_height, latent_width=latent_width)","handlingStrategy":"validation","validationCode":"tplf = latent_height * latent_width\nif memory_video_len % tplf != 0:\n    raise ValueError(f\"memory len {memory_video_len} not frame-aligned to {latent_height}x{latent_width}; fix tokenization config\")","typeGuard":"def is_frame_aligned(memory_video_len: int, lh: int, lw: int) -> bool:\n    tplf = lh * lw\n    return tplf > 0 and memory_video_len % tplf == 0","tryCatchPattern":null,"preventionTips":["Derive latent_height/latent_width and memory_video_len from the same VAE config object","Align any truncation of memory tokens to frame boundaries (multiples of lh*lw)"],"tags":["joy-echo","memory","rope","alignment","video-tokens"],"backgroundTag":"shape-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}