Skip to content

fix: text-only qwen3_5 checkpoints crash in get_rope_index (vision_config undefined) - #1818

Closed
eduardoworrel wants to merge 1 commit into
huggingface:mainfrom
eduardoworrel:fix/qwen3_5-text-only-rope
Closed

eduardoworrel wants to merge 1 commit into
huggingface:mainfrom
eduardoworrel:fix/qwen3_5-text-only-rope

Conversation

@eduardoworrel

@eduardoworrel eduardoworrel commented Oct 23, 2026 •

Copy link
Copy Markdown

Problem

Qwen3_5ForCausalLM throws in generate() for a text-only qwen3_5 checkpoint (model_type: "qwen3_5_text", no vision_config) when the ONNX graph exposes a position_ids input:

Cannot read properties of undefined (reading 'spatial_merge_size')
    at Function.get_rope_index (modeling_qwen2_vl.js)
    at Function.prepare_inputs_for_generation

Repro: AutoModelForCausalLM.from_pretrained(<qwen3_5_text checkpoint whose decoder exports position_ids>) then model.generate({ ...tokenizer('Hello'), max_new_tokens: 4 }). No image involved.

Cause

prepare_inputs_for_generation gates on the export:

const session = this.sessions["decoder_model_merged"] ?? this.sessions["model"];
if (!session.inputNames.includes("position_ids")) {
    return model_inputs;                       // checkpoint without the input: never reaches the crash
}
[model_inputs.position_ids, ...] = this.get_rope_index(...);   // checkpoint with it: crashes

get_rope_index (inherited from the Qwen2-VL lineage) then does:

const { vision_config } = this.config;
const spatial_merge_size = vision_config.spatial_merge_size ?? 2;   // dereferenced unconditionally
if (image_grid_thw || video_grid_thw) {                             // the value is only needed here

The ?? 2 fallback cannot apply — reading .spatial_merge_size on undefined throws first. Qwen3_5ForCausalLM extends Qwen3_5ForConditionalGeneration, which extends Qwen3VLForConditionalGeneration, so a text-only checkpoint inherits a multimodal get_rope_index that assumes a vision tower exists.

Checkpoints whose export omits position_ids are unaffected, which is why this only surfaces with certain exports.

Fix

-        const spatial_merge_size = vision_config.spatial_merge_size ?? 2;
+        const spatial_merge_size = vision_config?.spatial_merge_size ?? 2;

No behaviour change when vision_config exists; the value is only consumed inside the if (image_grid_thw || video_grid_thw) branch.

Verification

Same checkpoint, same call:

  • Without the patch: generate() throws Cannot read properties of undefined (reading 'spatial_merge_size') from get_rope_index.
  • With the patch and a rebuild: the call executes get_rope_index without throwing and proceeds past that point.

Scope of this patch is only the undefined dereference.

References

  • In transformers (Python) Qwen3_5ForCausalLM subclasses Qwen3_5PreTrainedModel and composes Qwen3_5TextModel — a sibling of the multimodal class, whose get_rope_index handles the text-only case. The JS port inherits the multimodal one instead.
  • Qwen2VLForConditionalGeneration documents that "CausalLM variants (e.g., Qwen2VLForCausalLM) extend this class but load only embed_tokens + decoder_model_merged (no vision_encoder)" — this dereference defeats that for a checkpoint with no vision_config at all.

… checkpoints

Qwen3_5ForCausalLM inherits get_rope_index from the Qwen2-VL lineage, where a vision
tower is always present. A text-only checkpoint (model_type qwen3_5_text) has no
vision_config, so reading vision_config.spatial_merge_size throws before the ?? 2
fallback can apply -- even for a pure-text generate() call.
@eduardoworrel
eduardoworrel marked this pull request as draft October 23, 2026 23:43
@eduardoworrel

Copy link
Copy Markdown
Author

Closing: the dereference is only reachable when the ONNX graph exports a position_ids input, and optimum-onnx (the supported exporter) does not export one for this architecture. Revisiting if that changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant