hiyouga/LlamaFactory · error · HTTPException
Not allowed
Error message
Not allowed
What it means
Megatron-Bridge datasets need assistant-only loss masks, expressed by wrapping assistant content in {% generation %}...{% endgeneration %}. _inject_generation_block (dataset_export.py:57) can only patch templates that branch with a literal `{% elif message['role'] == 'assistant' %}` block; if the chat template uses a different structure (loop over roles, set-based dispatch, filter blocks), injection is impossible and it raises ValueError.
Source
Thrown at src/llamafactory/api/app.py:104
@app.get(
"/v1/models",
response_model=ModelList,
status_code=status.HTTP_200_OK,
dependencies=[Depends(verify_api_key)],
)
async def list_models():
model_card = ModelCard(id=os.getenv("API_MODEL_NAME", "gpt-3.5-turbo"))
return ModelList(data=[model_card])
@app.post(
"/v1/chat/completions",
response_model=ChatCompletionResponse,
status_code=status.HTTP_200_OK,
dependencies=[Depends(verify_api_key)],
)
async def create_chat_completion(request: ChatCompletionRequest):
if not chat_model.engine.can_generate:
raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED, detail="Not allowed")
if request.stream:
generate = create_stream_chat_completion_response(request, chat_model)
return EventSourceResponse(generate, media_type="text/event-stream", sep="\n")
else:
return await create_chat_completion_response(request, chat_model)
@app.post(
"/v1/score/evaluation",
response_model=ScoreEvaluationResponse,
status_code=status.HTTP_200_OK,
dependencies=[Depends(verify_api_key)],
)
async def create_score_evaluation(request: ScoreEvaluationRequest):
if chat_model.engine.can_generate:
raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED, detail="Not allowed")
return await create_score_evaluation_response(request, chat_model)View on GitHub (pinned to f28afaf635)
Solutions
- Pass template: <name> in the YAML so a LlamaFactory registered template (which already formats correctly) is used instead of patching the tokenizer's native template
- Edit the tokenizer's chat_template.jinja to include an explicit `{% elif message['role'] == 'assistant' %}` branch, or pre-wrap assistant content with {% generation %} yourself
- Use a tokenizer whose template already contains {% generation %} blocks (mask-style templates)
Example fix
# before (chat_template.jinja)
{%- for message in messages %}{{ message.content }}{%- endfor %}
# no assistant elif branch -> ValueError
# after
{%- if message['role'] == 'user' %}{{ message.content }}
{%- elif message['role'] == 'assistant' %}{% generation %}{{ message.content }}{% endgeneration %}
{%- endif %} Defensive patterns
Strategy: validation
Validate before calling
import re
ASSISTANT_ELIF = re.compile(r"{%\s*elif\s+message\['role'\]\s*==\s*'assistant'\s*%}")
GEN = re.compile(r"{%\s*generation\s*%}")
if not GEN.search(chat_template):
assert ASSISTANT_ELIF.search(chat_template), (
'Chat template lacks an assistant elif branch; provide `template:` or add {% generation %} manually'
) Type guard
def template_is_injectable(chat_template: str) -> bool:
import re
return bool(re.search(r"{%\s*generation\s*%}", chat_template)) or bool(
re.search(r"{%\s*elif\s+message\['role'\]\s*==\s*'assistant'\s*%}", chat_template)
) Try / catch
try:
build_chat_template_with_generation(tokenizer_path, template_name=template)
except ValueError as e:
if 'Cannot inject' in str(e):
raise SystemExit('Set `template:` in YAML or wrap assistant content with {% generation %} in the tokenizer template') from e
raise Prevention
- Prefer LlamaFactory registered templates (`template:`) over native tokenizer templates for Megatron runs
- Keep chat templates with explicit role branches; validate with the regex above in CI when templates change
When it happens
Trigger: Megatron Bridge PT/SFT run where the tokenizer's Jinja chat template has no `{% elif message['role'] == 'assistant' %}` branch and no existing {% generation %} block; the regex _ASSISTANT_ELIF_REGEX finds no match.
Common situations: Custom tokenizers with hand-written templates; vendor templates that iterate roles in a for-loop or use `{% if message.role == ... %}` (attribute access instead of ['role']) so the regex misses; templates already using a different masking convention.
Related errors
- The model does not have a submodule named '{submodule_name}'
- Unsupported model type: {getattr(config, 'model_type')}.
- Stage does not supported: {stage}.
- Invalid length
- Only supports u/a/u/a/u...
AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14).
Data as JSON: /api/errors/82eac34b05da105d.
Report an issue: GitHub.