[Bug] Qwen3.5 system-only prompt still fails through Continuous Token
Environment
- verl main at
cf1649b2244a96c0ec7abea50cda9df291ee411b - transformers 5.10.4
- torch 2.11.0+cu130
- Qwen/Qwen3.5-9B processor
Reproduction
This reproducer follows the executor call used by AgentLoopWorker.ct_build_initial_tokens; it does not call apply_chat_template directly:
import asyncio
from transformers import AutoProcessor
from verl.utils.tokenizer.continuous_token_wiring import create_continuous_token_builder
MODEL = "/path/to/Qwen3.5-9B"
async def main():
processor = AutoProcessor.from_pretrained(MODEL, trust_remote_code=True)
builder = create_continuous_token_builder(
processor.tokenizer,
hf_model_type="qwen3_5",
processor=processor,
)
messages = [{"role": "system", "content": "system instruction"}]
return await asyncio.get_running_loop().run_in_executor(
None,
lambda: builder.build_initial_tokens(messages),
)
asyncio.run(main())Observed behavior
On clean main at cf1649b2:
builder: QwenVLContinuousTokenBuilder
jinja2.exceptions.TemplateError: System message must be at the beginning.The traceback goes through:
AgentLoopWorker.ct_build_initial_tokens
-> QwenVLContinuousTokenBuilder.build_initial_tokens
-> render_tokens_with_mm
-> verl.utils.tokenizer.chat_template.apply_chat_template
-> processor.apply_chat_templateThe same call on a patched tree returns 12 token IDs.
The native Qwen3.5 processor rejects a system-only history with No user query found in messages; the verl wrapper is expected to adapt this valid input by inserting and removing a synthetic user span.
Expected behavior
If system-only histories are supported for Continuous Token or SFT, the unified Continuous Token path should render the history without raising a template error. If they are intentionally unsupported, please document that constraint and identify the supported message shape.
This is a focused follow-up to #7725, isolating the behavior after Continuous Token became the default rollout path in #6804.
Source: verl-project/verl