halo-dev/halo · error · Error
No permission to upload attachment
Error message
No permission to upload attachment
What it means
Thrown in UploadDropdownItem.vue (a FormKit attachment input) when iterating selected files: the user has neither the system:attachments:manage nor the uc:attachments:manage permission. The component branches on permission — console upload API for system scope, UC API for user-center scope — and if neither permission is held, there is no valid upload endpoint, so it throws to abort the loop.
Source
Thrown at ui/src/formkit/inputs/attachment/UploadDropdownItem.vue:46
return;
}
const attachments: Attachment[] = [];
for (const file of files) {
if (utils.permission.has(["system:attachments:manage"])) {
const { data } =
await consoleApiClient.storage.attachment.uploadAttachmentForConsole({
file: file,
});
attachments.push(data);
} else if (utils.permission.has(["uc:attachments:manage"])) {
const { data } =
await ucApiClient.storage.attachment.uploadAttachmentForUc({
file: file,
});
attachments.push(data);
} else {
throw new Error("No permission to upload attachment");
}
}
emit("selected", attachments);
});
</script>
<template>
<VDropdownItem @click="openFileInputDialog()">
{{ $t("core.common.buttons.upload") }}
</VDropdownItem>
</template>
View on GitHub (pinned to d2f5165f9c)
Solutions
- Grant the role the appropriate permission: uc:attachments:manage for user-center uploads or system:attachments:manage for console uploads (System → Roles → edit role → Permissions).
- Hide the UploadDropdownItem entirely when the user lacks upload permission — gate its rendering on utils.permission.has([...]) so the option never appears.
- Catch the thrown error in the caller and show a localized 'no permission' toast instead of letting it propagate as an unhandled rejection.
- Verify the permission identifiers used match the role template definitions (typos in 'uc:attachments:manage' silently fail).
Example fix
// before
} else {
throw new Error("No permission to upload attachment");
}
// after — surface a localized message and avoid rendering the item for unauthorized users
} else {
Toast.warning(t("core.attachment.no_upload_permission"));
return;
}
// and in the template, gate the dropdown item:
// <VDropdownItem v-if="canUpload" @click="openFileInputDialog()"> Defensive patterns
Strategy: validation
Validate before calling
// Check upload permission before showing the picker / iterating files
const canUploadConsole = utils.permission.has(["system:attachments:manage"]);
const canUploadUc = utils.permission.has(["uc:attachments:manage"]);
if (!canUploadConsole && !canUploadUc) {
Toast.warning(t("core.attachment.no_upload_permission"));
return;
}
// then proceed with the upload loop Type guard
function canUploadAttachment(): boolean {
return (
utils.permission.has(["system:attachments:manage"]) ||
utils.permission.has(["uc:attachments:manage"])
);
} Try / catch
onFileInputChange(async (files) => {
try {
// ... upload loop that may throw 'No permission to upload attachment'
} catch (e) {
Toast.error(e instanceof Error ? e.message : "Upload failed");
}
}); Prevention
- Grant the role uc:attachments:manage (user-center) or system:attachments:manage (console).
- Hide the UploadDropdownItem via v-if="canUploadAttachment()" so unauthorized users never see the option.
- Verify permission identifiers match the role template exactly — typos silently fail the check.
When it happens
Trigger: A user opens the attachment upload dropdown in a FormKit input (e.g. editor attachment picker) and selects files, but their role lacks both system:attachments:manage and uc:attachments:manage. The permission checks via utils.permission.has both return false, so the upload is rejected client-side before any network call.
Common situations: A logged-in user whose role was not granted any attachment-upload permission tries to upload through a FormKit field; permissions changed/migrated and the role lost uc:attachments:manage; the component is shown to a user who should not see it (missing UI permission gate); a custom role with a typo'd permission identifier.
Related errors
- Attachment has no permalink
- Policy name is required
- PluginModule.formkit must be an object.
- Required url is missing.
- Policy name must not be blank
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/a07096eeb1e6522f.
Report an issue: GitHub.