halo-dev/halo · error · Error
File is required
Error message
File is required
What it means
Thrown inside the onConfirm callback of a Dialog in LocalUpload.vue's theme-install 'already exists' flow. When a theme ZIP is uploaded but the theme already exists, the backend returns THEME_ALREADY_EXISTS_TYPE; the handler asks the user to confirm an upgrade and needs the original File to call consoleApiClient.theme.theme.upgradeTheme. If file is undefined (the Uppy upload's file.data was not retained), the upgrade cannot proceed and this throws.
Source
Thrown at ui/console-src/modules/interface/themes/components/list-tabs/LocalUpload.vue:57
if (body?.type === THEME_ALREADY_EXISTS_TYPE) {
handleCatchExistsException(body, file?.data as File | undefined);
}
};
const handleCatchExistsException = async (
error: ThemeInstallationErrorResponse,
file?: File
) => {
Dialog.info({
title: t("core.theme.operations.existed_during_installation.title"),
description: t(
"core.theme.operations.existed_during_installation.description"
),
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
onConfirm: async () => {
if (!file) {
throw new Error("File is required");
}
await consoleApiClient.theme.theme.upgradeTheme({
name: error.themeName,
file: file,
});
Toast.success(t("core.common.toast.upgrade_success"));
queryClient.invalidateQueries({ queryKey: ["themes"] });
themeStore.fetchActivatedTheme();
activeTabId.value = "installed";
},
});
};
</script>
View on GitHub (pinned to d2f5165f9c)
Solutions
- Retain the uploaded File reference: capture it during Uppy's file-added/upload-success events into a ref, and pass that into handleCatchExistsException instead of relying on onError's file?.data.
- Before showing the upgrade dialog, verify file is a File instance; if not, re-prompt the user to re-select the theme ZIP.
- Confirm the Uppy configuration preserves file.data through to the onError callback (check the @halo-dev upload wrapper version).
- Catch the throw in onConfirm so the Dialog doesn't surface an unhandled rejection; show a Toast asking the user to re-upload.
Example fix
// before
onConfirm: async () => {
if (!file) {
throw new Error("File is required");
}
await consoleApiClient.theme.theme.upgradeTheme({ name: error.themeName, file });
// after — guard with user-facing feedback instead of throwing inside the dialog callback
onConfirm: async () => {
if (!(file instanceof File)) {
Toast.error(t("core.theme.operations.existed_during_installation.reselect"));
return;
}
await consoleApiClient.theme.theme.upgradeTheme({ name: error.themeName, file }); Defensive patterns
Strategy: type-guard
Validate before calling
// Retain the File and verify it before the upgrade call
const uploadedFile = ref<File | null>(null);
// on Uppy 'file-added': uploadedFile.value = file.data;
function canUpgradeTheme(f: File | null | undefined): f is File {
return f instanceof File;
}
// in onConfirm: if (!canUpgradeTheme(uploadedFile.value)) { reselectPrompt(); return; } Type guard
function isFile(f: unknown): f is File {
return typeof File !== "undefined" && f instanceof File;
} Try / catch
onConfirm: async () => {
try {
if (!isFile(file)) throw new Error("File is required");
await consoleApiClient.theme.theme.upgradeTheme({ name: error.themeName, file });
} catch (e) {
Toast.error(e instanceof Error ? e.message : "Upgrade failed");
}
} Prevention
- Capture the uploaded File in a ref during Uppy's file-added event rather than relying on onError's file.data lifecycle.
- Confirm the Uppy wrapper preserves file.data through to onError/onConfirm after version upgrades.
- Re-prompt the user to re-select the ZIP if the File reference is gone by confirm time.
When it happens
Trigger: Uppy's onError fires with a body.type matching the already-exists error, but file?.data (the original File object) is undefined — e.g. the file reference was garbage-collected, the upload error occurred before file.data was attached, or the Uppy config strips file data after completion. The user confirms the upgrade dialog and onConfirm throws 'File is required'.
Common situations: Theme already installed and user tries to re-install via the local upload tab; Uppy version change altered the file lifecycle so file.data is gone by the time onError/onConfirm runs; a large theme file was cleared from memory; custom Uppy plugin that doesn't retain the File object in the error payload.
Related errors
- Please select a snapshot
- Please select two snapshots to compare
- Attachment has no permalink
- Policy name is required
- No permission to upload attachment
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/88c153351aa8896b.
Report an issue: GitHub.