{"record":{"id":"9357d6c180b9388b","repo":"apache/superset","slug":"you-do-not-have-permission-to-manage-tags-on-obje","errorCode":null,"errorMessage":"You do not have permission to manage tags on {object_type.name}s","messagePattern":"You do not have permission to manage tags on (.+?)s","errorType":"exception","errorClass":"TagForbiddenError","httpStatus":403,"severity":"error","filePath":"superset/commands/utils.py","lineNumber":241,"sourceCode":"\n    # `tags` not part of the update payload\n    if new_tag_ids is None:\n        return\n\n    # No changes in the list\n    current_custom_tags = [tag.id for tag in current_tags if tag.type == TagType.custom]\n    if Counter(current_custom_tags) == Counter(new_tag_ids):\n        return\n\n    # No perm to tags assets\n    if not (\n        security_manager.can_access(\"can_write\", \"Tag\")\n        or security_manager.can_access(\"can_tag\", object_type.name.capitalize())\n    ):\n        validation_error = (\n            f\"You do not have permission to manage tags on {object_type.name}s\"\n        )\n        raise TagForbiddenError(validation_error)\n\n    # Validate if new tags already exist\n    additional_tags = [tag for tag in new_tag_ids if tag not in current_custom_tags]\n    for tag_id in additional_tags:\n        if not TagDAO.find_by_id(tag_id):\n            validation_error = f\"Tag ID {tag_id} not found\"\n            raise TagNotFoundValidationError(validation_error)\n\n    return\n\n\ndef update_tags(\n    object_type: ObjectType,\n    object_id: int,\n    current_tags: list[Tag],\n    new_tag_ids: list[int],\n) -> None:\n    \"\"\"","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/utils.py#L223-L259","documentation":"validate_tags raises TagForbiddenError when the caller tries to change custom tags on an object but lacks both 'can_write' on the Tag resource and 'can_tag' on the specific object type. Superset splits tag authority: full Tag writers can manage all tag associations; per-object-type 'can_tag' holders may only associate existing tags.","triggerScenarios":"PUT/PATCH on a dashboard/chart/dataset with a changed 'tags' list while the user's role has neither can_write on Tag nor can_tag on Dashboard/Chart/etc. Also triggered by tag assignment endpoints when permissions were trimmed.","commonSituations":"Custom roles built without copying the can_tag permissions; recent role refactors dropping Tag permissions; Gamma-style users attempting to tag after an upgrade that introduced the can_tag model.","solutions":["Grant the role can_tag on the object type (e.g. 'can_tag on Dashboard') or can_write on Tag.","If the user should not tag at all, remove tag editing from their UI flow so the request is never sent.","Audit role permissions with the /api/v1/permissions or role API to confirm the exact missing permission."],"exampleFix":"# before\ncurl -X PATCH .../api/v1/dashboard/1 -d '{\"tags\":[3]}'  # 403 TagForbiddenError\n\n# after (admin grants permission first)\nflask fab add-permissions -r TagEditor -p can_write -r Tag  # or add can_tag on Dashboard","handlingStrategy":"validation","validationCode":"from superset import security_manager\nok = security_manager.can_access(\"can_write\", \"Tag\") or security_manager.can_access(\n    \"can_tag\", object_type.capitalize()\n)\nif not ok:\n    raise PermissionError(\"tag management not permitted\")","typeGuard":null,"tryCatchPattern":"from superset.commands.tag.exceptions import TagForbiddenError\ntry:\n    update_tags(...)\nexcept TagForbiddenError:\n    skip_tag_update_keep_object_save()","preventionTips":["Check can_tag/can_write before exposing tag UI to a role","Keep tag mutation in a separate optional request so object save is not blocked"],"tags":["tags","permissions","rbac","forbidden","backend"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}