{"record":{"id":"614e3d44868e8344","repo":"infiniflow/ragflow","slug":"each-template-must-include-a-name-kind-and-confi","errorCode":null,"errorMessage":"Each template must include a name, kind, and config object.","messagePattern":"Each template must include a name, kind, and config object\\.","errorType":"validation","errorClass":"GroupValidationError","httpStatus":null,"severity":"error","filePath":"api/db/services/compilation_template_group_service.py","lineNumber":325,"sourceCode":"                current_by_id = {child.id: child for child in current_children}\n                retained_ids: set[str] = set()\n                seen_names: set[str] = set()\n\n                for index, child in enumerate(templates):\n                    child_id = str((child or {}).get(\"id\") or \"\").strip()\n                    target = current_by_id.get(child_id) if child_id else None\n                    if child_id and target is None:\n                        raise GroupValidationError(f\"Template {child_id} does not belong to this group.\")\n                    # Older clients did not send child ids. Preserve their\n                    # existing ids by matching the submitted order.\n                    if target is None and not child_id and index < len(current_children):\n                        target = current_children[index]\n\n                    kind = str((child or {}).get(\"kind\") or \"\").strip()\n                    name = str((child or {}).get(\"name\") or \"\").strip()\n                    config = (child or {}).get(\"config\") or {}\n                    if not kind or not name or not isinstance(config, dict):\n                        raise GroupValidationError(\"Each template must include a name, kind, and config object.\")\n                    if name in seen_names:\n                        raise GroupValidationError(f\"Template name '{name}' is duplicated in this group.\")\n                    seen_names.add(name)\n\n                    from api.db.services.compilation_template_service import CompilationTemplateService\n\n                    config = CompilationTemplateService.fill_config_default_llm(config, tenant_id)\n                    duplicate_query = CompilationTemplate.select().where(\n                        CompilationTemplate.tenant_id == tenant_id,\n                        CompilationTemplate.group_id == group_id,\n                        CompilationTemplate.name == name,\n                        ~CompilationTemplate.is_builtin,\n                        CompilationTemplate.status == StatusEnum.VALID.value,\n                    )\n                    if target is not None:\n                        duplicate_query = duplicate_query.where(CompilationTemplate.id != target.id)\n                    if duplicate_query.exists():\n                        raise GroupValidationError(f\"Template name '{name}' already exists. Please choose another name.\")","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/api/db/services/compilation_template_group_service.py#L307-L343","documentation":"GroupValidationError raised during group update when a submitted template entry is missing kind, name, or its config is not a dict. The fields are coerced with str(...).strip(), so empty/whitespace strings and null names or kinds fail, and any non-dict config (list, string, null that defaults to {} is fine) fails the isinstance check.","triggerScenarios":"Group save payload containing a template with empty name, empty kind, or config as a non-object (e.g. a JSON string or array). Note config:null passes because the code substitutes {}.","commonSituations":"Form submitted before required fields filled; client sending config as a JSON-encoded string instead of an object; partial template object appended by buggy frontend state management.","solutions":["Ensure every template entry has a non-empty name and kind and config is a JSON object.","Validate the payload shape client-side before the request (see typeGuard below).","Check for accidental double-JSON-encoding of config (config: '\"{\\\"chunk_token_num\\\":...}\"')."],"exampleFix":"// before\n{\"name\": \"\", \"kind\": \"tree\", \"config\": \"{}\"}\n// after\n{\"name\": \"My template\", \"kind\": \"tree\", \"config\": {}}","handlingStrategy":"type-guard","validationCode":"def template_entry_ok(t):\n    return (\n        isinstance(t, dict)\n        and bool(str(t.get('kind') or '').strip())\n        and bool(str(t.get('name') or '').strip())\n        and isinstance(t.get('config') or {}, dict)\n    )\n\nassert all(template_entry_ok(t) for t in payload['templates'])","typeGuard":"type TemplateEntry = { name: string; kind: string; config: Record<string, unknown> };\nfunction isTemplateEntry(t: unknown): t is TemplateEntry {\n  return (\n    !!t && typeof t === 'object' &&\n    typeof (t as any).name === 'string' && (t as any).name.trim() !== '' &&\n    typeof (t as any).kind === 'string' && (t as any).kind.trim() !== '' &&\n    typeof (t as any).config === 'object' && (t as any).config !== null && !Array.isArray((t as any).config)\n  );\n}","tryCatchPattern":null,"preventionTips":["Make template forms require name/kind before enabling save.","Ensure config is sent as a JSON object, never a serialized string."],"tags":["validation","compilation-template","payload-shape"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}