{"record":{"id":"ab92236b3ef7fba4","repo":"nextcloud/server","slug":"failed-to-set-tag-for-file","errorCode":null,"errorMessage":"Failed to set tag for file","messagePattern":"Failed to set tag for file","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/systemtags/src/services/files.ts","lineNumber":69,"sourceCode":"}\n\n/**\n * Set a tag for a given file (by id).\n *\n * @param tag - The tag to set\n * @param fileId - The id of the file to set the tag for\n */\nexport async function setTagForFile(tag: TagWithId | ServerTagWithId, fileId: number): Promise<void> {\n\tconst path = '/systemtags-relations/files/' + fileId + '/' + tag.id\n\tconst tagToPut = formatTag(tag)\n\ttry {\n\t\tawait davClient.customRequest(path, {\n\t\t\tmethod: 'PUT',\n\t\t\tdata: tagToPut,\n\t\t})\n\t} catch (error) {\n\t\tlogger.error(t('systemtags', 'Failed to set tag for file'), { error })\n\t\tthrow new Error(t('systemtags', 'Failed to set tag for file'), { cause: error })\n\t}\n}\n\n/**\n * Delete a tag for a given file (by id).\n *\n * @param tag - The tag to delete\n * @param fileId - The id of the file to delete the tag for\n */\nexport async function deleteTagForFile(tag: TagWithId, fileId: number): Promise<void> {\n\tconst path = '/systemtags-relations/files/' + fileId + '/' + tag.id\n\ttry {\n\t\tawait davClient.deleteFile(path)\n\t} catch (error) {\n\t\tlogger.error(t('systemtags', 'Failed to delete tag for file'), { error })\n\t\tthrow new Error(t('systemtags', 'Failed to delete tag for file'), { cause: error })\n\t}\n}","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/systemtags/src/services/files.ts#L51-L87","documentation":"Thrown by setTagForFile() when the PUT to /systemtags-relations/files/{fileId}/{tagId} fails. This operation attaches an existing tag to a file, and the server enforces both file access and the tag's can-assign permission; failures are wrapped with the original WebDAVClientError as `cause`.","triggerScenarios":"PUT failing with 403 when the tag is not user-assignable (oc:user-assignable / oc:can-assign false for this user), 404 when either fileId or tagId no longer exists, 401 after session expiry, or 5xx in maintenance mode.","commonSituations":"Assigning an admin-only (non-assignable) tag as a regular user; tagging a file that was just deleted or whose share expired; stale tag id from an old tag list; concurrent removal of the tag in another session.","solutions":["Pre-check the tag's canAssign/userAssignable flag from fetchTags() and hide or disable non-assignable tags in the picker","Verify both ids are current (file node from the active view, tag from a fresh fetchTags())","Inspect error.cause status: 403 → permission, 404 → stale id, 401 → re-login","For admin-only tags run confirmPassword() before the DAV call"],"exampleFix":"// before\nawait setTagForFile(tag, fileId) // 403 if tag is not user-assignable\n\n// after\nif (!tag.canAssign && !isAdmin) {\n\tshowError(t('systemtags', 'You are not allowed to assign this tag'))\n} else {\n\tawait setTagForFile(tag, fileId)\n}","handlingStrategy":"validation","validationCode":"// Pre-check assignability — fetchTags returns oc:can-assign per tag\nimport { fetchTags, setTagForFile } from './services'\n\nasync function assignTagSafely(tag: TagWithId, fileId: number) {\n\tconst fresh = (await fetchTags()).find((t) => t.id === tag.id)\n\tif (!fresh) throw new Error('stale tag id')\n\tif (!fresh.userAssignable) {\n\t\tthrow new Error('tag is not assignable by users')\n\t}\n\tawait setTagForFile(fresh, fileId)\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait setTagForFile(tag, fileId)\n} catch (error) {\n\tconst status = (error.cause as WebDAVClientError | undefined)?.response?.status\n\tif (status === 403) { /* can-assign denied */ }\n\telse if (status === 404) { /* file or tag gone — refresh */ }\n\telse throw error\n}","preventionTips":["Filter the tag picker to tags where canAssign/userAssignable is true","Verify fileId is live before offering tag actions","Run confirmPassword() when admins assign restricted tags"],"tags":["systemtags","webdav","put","tag-assignment","typescript"],"backgroundTag":"webdav-put-failed","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}