{"record":{"id":"4241dd057c10d070","repo":"infiniflow/ragflow","slug":"a-template-group-must-contain-at-least-one-templat","errorCode":null,"errorMessage":"A template group must contain at least one template.","messagePattern":"A template group must contain at least one template\\.","errorType":"validation","errorClass":"GroupValidationError","httpStatus":null,"severity":"error","filePath":"api/db/services/compilation_template_group_service.py","lineNumber":40,"sourceCode":"from common.misc_utils import get_uuid\n\n\nSCOPE_FILE = \"file\"\nSCOPE_DATASET = \"dataset\"\n\n\nclass GroupValidationError(ValueError):\n    pass\n\n\ndef _derive_scope(templates: list[dict]) -> str:\n    \"\"\"Derive the group's scope from its child templates.\n\n    One artifacts child = dataset scope (and must be the only child).\n    Otherwise file scope, with no artifacts allowed.\n    \"\"\"\n    if not templates:\n        raise GroupValidationError(\"A template group must contain at least one template.\")\n    kinds = [str((t or {}).get(\"kind\") or \"\").strip() for t in templates]\n    artifact_count = sum(1 for k in kinds if k == \"wiki\")\n    if artifact_count > 0:\n        if artifact_count != 1 or len(templates) != 1:\n            raise GroupValidationError(\"A wiki template cannot be combined with other templates in the same group.\")\n        return SCOPE_DATASET\n\n    _enforce_single_rechunk_tree(templates)\n    return SCOPE_FILE\n\n\ndef _enforce_single_rechunk_tree(templates: list[dict]) -> None:\n    \"\"\"At most one tree-kind child in the group may enable re-chunking.\n\n    Re-chunking soft-deletes the doc's original chunks via\n    ``available_int=0`` and inserts merged replacements; running two\n    such templates would race on the same source chunks and produce\n    non-deterministic output. Per-tenant invariant is enforced","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/api/db/services/compilation_template_group_service.py#L22-L58","documentation":"GroupValidationError (a ValueError subclass) raised by _derive_scope in the compilation template group service when the templates list passed to group creation/update is empty. A group's scope is derived from its children (one wiki child => dataset scope, otherwise file scope), so zero children cannot derive any scope and is rejected before persistence.","triggerScenarios":"POST/PUT of a template group whose 'templates' array is empty, null-ish, or filtered to nothing during request processing; deleting all children of a group and saving; payload construction bug that drops the templates field.","commonSituations":"Frontend form allowing save with no selected templates; bulk imports generating empty groups; API clients sending {\"templates\": []} to create a placeholder group.","solutions":["Include at least one template (kind 'wiki' alone for dataset scope, or file-scope kinds) in the group payload.","Validate templates is a non-empty array client-side before submitting.","If you meant to remove a group, delete the group instead of emptying it.","Check request-building code for filters that can empty the list."],"exampleFix":"// before\nawait api.createTemplateGroup({ name, templates: [] });\n\n// after\nif (!templates.length) throw new Error('Select at least one template for the group');\nawait api.createTemplateGroup({ name, templates });","handlingStrategy":"validation","validationCode":"if not templates or not isinstance(templates, list):\n    raise ValueError(\"templates must be a non-empty list\")","typeGuard":"def valid_group_payload(templates) -> bool:\n    return isinstance(templates, list) and len(templates) > 0","tryCatchPattern":"try:\n    create_group(name, templates)\nexcept GroupValidationError as e:\n    return bad_request(str(e))  # user-input error, never retry","preventionTips":["Require >=1 selected template in the group editor before enabling Save.","Delete groups instead of emptying them.","GroupValidationError is a ValueError subclass — catch it specifically for clean 400 responses."],"tags":["validation","template-group","compilation","ragflow"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}