{"record":{"id":"fe7fc0a3e3fe527a","repo":"apache/superset","slug":"duplicate-folder-name-name","errorCode":null,"errorMessage":"Duplicate folder name: {name}","messagePattern":"Duplicate folder name: (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":422,"severity":"error","filePath":"superset/commands/dataset/update.py","lineNumber":462,"sourceCode":"    queue: list[tuple[FolderSchema, list[UUID]]] = [(folder, []) for folder in folders]\n    seen_uuids = set()\n    seen_fqns = set()  # fully qualified folder names\n    while queue:\n        obj, path = queue.pop(0)\n        uuid, name = obj[\"uuid\"], obj.get(\"name\")\n\n        if uuid in path:\n            raise ValidationError(f\"Cycle detected: {uuid} appears in its ancestry\")\n\n        if uuid in seen_uuids:\n            raise ValidationError(f\"Duplicate UUID in folder structure: {uuid}\")\n        seen_uuids.add(uuid)\n\n        # folders can have duplicate name as long as they're not siblings\n        if name:\n            fqn = tuple(path + [name])\n            if name and fqn in seen_fqns:\n                raise ValidationError(f\"Duplicate folder name: {name}\")\n            seen_fqns.add(fqn)\n\n            # Allow default folders (by UUID) to use reserved names\n            if (\n                name.lower() in {\"metrics\", \"columns\"}\n                and str(uuid) not in DEFAULT_FOLDER_UUIDS\n            ):\n                raise ValidationError(f\"Folder cannot have name '{name}'\")\n\n        # check if metric/column UUID exists (skip default folders)\n        elif (\n            not name\n            and uuid not in valid_uuids\n            and str(uuid) not in DEFAULT_FOLDER_UUIDS\n        ):\n            raise ValidationError(f\"Invalid UUID: {uuid}\")\n\n        # traverse children","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/dataset/update.py#L444-L480","documentation":"Raised by validate_folders() when two folders share the same fully-qualified name — i.e. the same name among siblings (same parent path). Duplicate names are allowed only in different branches; seen_fqns keys on tuple(path + [name]) so the same name under the same parent collides.","triggerScenarios":"PUT /api/v1/dataset/{id} with two sibling folders both named e.g. 'Sales' under the same parent, where at least one carries an explicit name.","commonSituations":"Renaming a folder to match its sibling. Merging folder trees where both sources had a child with the same name under the equivalent parent.","solutions":["Rename one of the colliding siblings (the message names it) so names are unique within the parent.","Or move one folder under a different parent where the name is free.","Check the fqn (full path) semantics: only same-parent duplicates are rejected."],"exampleFix":"# before\n[{name: \"Sales\", children: [{name: \"Q1\"}, {name: \"Q1\"}]}]\n# after\n[{name: \"Sales\", children: [{name: \"Q1\"}, {name: \"Q1 (archive)\"]}]}","handlingStrategy":"validation","validationCode":"def sibling_name_clash(folders):\n    stack = [(f, ()) for f in folders]\n    seen = set()\n    while stack:\n        node, path = stack.pop()\n        if (name := node.get(\"name\")):\n            fqn = path + (name,)\n            if fqn in seen:\n                return name\n            seen.add(fqn)\n            path = fqn\n        stack.extend((c, path) for c in node.get(\"children\", []))\n    return None\n\nassert sibling_name_clash(folders) is None","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Rename on collision instead of assuming server-side uniquification.","Only same-parent names clash; branching is a valid workaround.","Validate merges of folder trees for sibling collisions."],"tags":["dataset","folders","validation","naming"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}