{"record":{"id":"82eac34b05da105d","repo":"hiyouga/LlamaFactory","slug":"not-allowed","errorCode":null,"errorMessage":"Not allowed","messagePattern":"Not allowed","errorType":"http","errorClass":"HTTPException","httpStatus":405,"severity":"error","filePath":"src/llamafactory/api/app.py","lineNumber":104,"sourceCode":"    @app.get(\n        \"/v1/models\",\n        response_model=ModelList,\n        status_code=status.HTTP_200_OK,\n        dependencies=[Depends(verify_api_key)],\n    )\n    async def list_models():\n        model_card = ModelCard(id=os.getenv(\"API_MODEL_NAME\", \"gpt-3.5-turbo\"))\n        return ModelList(data=[model_card])\n\n    @app.post(\n        \"/v1/chat/completions\",\n        response_model=ChatCompletionResponse,\n        status_code=status.HTTP_200_OK,\n        dependencies=[Depends(verify_api_key)],\n    )\n    async def create_chat_completion(request: ChatCompletionRequest):\n        if not chat_model.engine.can_generate:\n            raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED, detail=\"Not allowed\")\n\n        if request.stream:\n            generate = create_stream_chat_completion_response(request, chat_model)\n            return EventSourceResponse(generate, media_type=\"text/event-stream\", sep=\"\\n\")\n        else:\n            return await create_chat_completion_response(request, chat_model)\n\n    @app.post(\n        \"/v1/score/evaluation\",\n        response_model=ScoreEvaluationResponse,\n        status_code=status.HTTP_200_OK,\n        dependencies=[Depends(verify_api_key)],\n    )\n    async def create_score_evaluation(request: ScoreEvaluationRequest):\n        if chat_model.engine.can_generate:\n            raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED, detail=\"Not allowed\")\n\n        return await create_score_evaluation_response(request, chat_model)","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/api/app.py#L86-L122","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"# before (chat_template.jinja)\n{%- for message in messages %}{{ message.content }}{%- endfor %}\n# no assistant elif branch -> ValueError\n\n# after\n{%- if message['role'] == 'user' %}{{ message.content }}\n{%- elif message['role'] == 'assistant' %}{% generation %}{{ message.content }}{% endgeneration %}\n{%- endif %}","handlingStrategy":"validation","validationCode":"import re\nASSISTANT_ELIF = re.compile(r\"{%\\s*elif\\s+message\\['role'\\]\\s*==\\s*'assistant'\\s*%}\")\nGEN = re.compile(r\"{%\\s*generation\\s*%}\")\nif not GEN.search(chat_template):\n    assert ASSISTANT_ELIF.search(chat_template), (\n        'Chat template lacks an assistant elif branch; provide `template:` or add {% generation %} manually'\n    )","typeGuard":"def template_is_injectable(chat_template: str) -> bool:\n    import re\n    return bool(re.search(r\"{%\\s*generation\\s*%}\", chat_template)) or bool(\n        re.search(r\"{%\\s*elif\\s+message\\['role'\\]\\s*==\\s*'assistant'\\s*%}\", chat_template)\n    )","tryCatchPattern":"try:\n    build_chat_template_with_generation(tokenizer_path, template_name=template)\nexcept ValueError as e:\n    if 'Cannot inject' in str(e):\n        raise SystemExit('Set `template:` in YAML or wrap assistant content with {% generation %} in the tokenizer template') from e\n    raise","preventionTips":["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"],"tags":["megatron-bridge","chat-template","jinja","dataset"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}