{"record":{"id":"242f613982324a17","repo":"passbolt/passbolt_api","slug":"you-do-not-have-the-permission-to-change-a-personal-tag-into","errorCode":null,"errorMessage":"You do not have the permission to change a personal tag into shared tag.","messagePattern":"You do not have the permission to change a personal tag into shared tag\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/Tags/src/Service/Tags/UpdatePersonalTagService.php","lineNumber":93,"sourceCode":"                $options\n            );\n\n            if (!$tagsTable->save($entity)) {\n                throw new CustomValidationException(\n                    __('Unable to save the tag.'),\n                    $entity->getErrors(),\n                    $tagsTable\n                );\n            }\n\n            return $entity;\n        } else {\n            $this->assertV4TagCreationEnabled();\n\n            $slug = $dtoArray['slug'];\n\n            if (!is_null($slug) && mb_substr($slug, 0, 1) === '#') {\n                throw new BadRequestException(\n                    __('You do not have the permission to change a personal tag into shared tag.')\n                );\n            }\n\n            $this->assertV4DowngradeAllowed($tag);\n\n            /** @var \\Passbolt\\Tags\\Model\\Table\\ResourcesTagsTable $resourcesTagsTable */\n            $resourcesTagsTable = $this->fetchTable('Passbolt/Tags.ResourcesTags');\n\n            return $tagsTable->getConnection()->transactional(function () use (\n                $tagsTable,\n                $resourcesTagsTable,\n                $tag,\n                $slug,\n                $uac\n            ) {\n                $newTag = $tagsTable->findOrCreateTag($slug, $uac);\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Tags/src/Service/Tags/UpdatePersonalTagService.php#L75-L111","documentation":"This BadRequestException is thrown by UpdatePersonalTagService::update when a user attempts to update a personal (V4) tag but supplies a slug starting with '#', the prefix that marks a tag as shared. Personal tags can only be converted to shared tags via the V5 metadata flow, so the V4 update endpoint rejects the transition outright rather than silently promoting the tag.","triggerScenarios":"PATCH/PUT to the tag update endpoint with a personal tag whose DTO slug is non-null and begins with '#' (mb_substr($slug, 0, 1) === '#'), while V4 tag creation is still enabled.","commonSituations":"A client or script batch-renames tags and accidentally adds the '#' shared prefix; a user tries to share a personal tag using the legacy V4 API after migrating partially to V5 metadata; automation copies shared-tag slugs onto personal tags.","solutions":["Remove the leading '#' from the slug, or omit the slug entirely, when updating a personal tag via the V4 endpoint.","If the goal is to make the tag shared, migrate to the V5 metadata tag workflow (MetadataTag upgrade services) instead of editing the slug.","Check the tag's is_shared/user_id ownership before sending an update so clients can render the correct UI (no shared toggle for personal tags in V4)."],"exampleFix":"// before\nawait updateTag(tagId, { slug: '#team-credentials' });\n// after\nawait updateTag(tagId, { slug: 'team-credentials' }); // keep personal, or use V5 metadata API to share","handlingStrategy":"validation","validationCode":"if (typeof slug === 'string' && slug.startsWith('#')) throw new Error('Cannot set a shared-prefixed slug on a personal tag');","typeGuard":"const isPersonalSlug = (slug: unknown): slug is string => typeof slug === 'string' && !slug.startsWith('#');","tryCatchPattern":null,"preventionTips":["Strip or reject '#' prefixes for personal tags in client-side form validation.","Track tag ownership (is_shared/user_id) in UI state before enabling rename.","Use the V5 metadata API for any personal-to-shared conversion."],"tags":["tags","bad-request","permissions","v4-api"],"backgroundTag":"permission-denied","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}