{"record":{"id":"9871f0a725ae4844","repo":"TryGhost/Ghost","slug":"too-many-files","errorCode":"too_many_files","errorMessage":"This theme archive contains too many files for the browser editor (${entries.length}/${THEME_EDITOR_ARCHIVE_LIMITS.maxFiles}).","messagePattern":"This theme archive contains too many files for the browser editor \\((.+?)/(.+?)\\)\\.","errorType":"validation","errorClass":"ThemeArchiveExtractionError","httpStatus":null,"severity":"warning","filePath":"apps/admin/src/settings/app/components/settings/site/theme/theme-editor-utils.ts","lineNumber":189,"sourceCode":"\n    return `${megabytes.toFixed(1)} MB`;\n};\n\nconst collectArchiveEntries = (zip: JSZip) => {\n    const entries: Array<[string, JSZip.JSZipObject]> = [];\n\n    zip.forEach((relativePath, entry) => {\n        if (!entry.dir) {\n            entries.push([relativePath, entry]);\n        }\n    });\n\n    return entries;\n};\n\nconst assertThemeArchiveLimits = (entries: Array<[string, JSZip.JSZipObject]>) => {\n    if (entries.length > THEME_EDITOR_ARCHIVE_LIMITS.maxFiles) {\n        throw new ThemeArchiveExtractionError(\n            'too_many_files',\n            `This theme archive contains too many files for the browser editor (${entries.length}/${THEME_EDITOR_ARCHIVE_LIMITS.maxFiles}).`\n        );\n    }\n};\n\nconst loadThemeArchive = async (arrayBuffer: ArrayBuffer) => {\n    try {\n        return await JSZip.loadAsync(arrayBuffer);\n    } catch {\n        throw new ThemeArchiveExtractionError('invalid_archive', invalidArchiveMessage);\n    }\n};\n\nconst readThemeBinaryFile = async (entry: JSZip.JSZipObject) => {\n    try {\n        return await entry.async('uint8array');\n    } catch {","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/admin/src/settings/app/components/settings/site/theme/theme-editor-utils.ts#L171-L207","documentation":"Thrown by assertThemeArchiveLimits in theme-editor-utils when a uploaded theme .zip contains more than THEME_EDITOR_ARCHIVE_LIMITS.maxFiles (1000) non-directory entries. It is a browser-side guard: the in-browser theme editor (JSZip-based) is not designed to ingest huge archives (e.g. ones accidentally bundling node_modules or generated asset variants), so extraction is aborted before memory/time becomes a problem. Thrown as ThemeArchiveExtractionError with reason 'too_many_files'.","triggerScenarios":"extractThemeArchive is called with an ArrayBuffer whose zip contains > 1000 file entries (directories excluded by collectArchiveEntries). Common: a theme zip that included node_modules, a .git directory, generated/image-sprite folders, or multiple bundled themes.","commonSituations":"Theme developer ran a build that emitted thousands of files; user zipped the whole project folder instead of the dist output; a downloaded third-party theme ships heavy asset directories; CI artifact accidentally includes dev dependencies.","solutions":["Re-zip the theme excluding node_modules, .git, and large generated asset directories so it contains only the theme files Ghost needs.","Verify the file count locally before upload: unzip -l theme.zip | tail -1 (or JSZip inspection) to confirm it's under 1000 entries.","If the limit is genuinely too low for a legitimate theme, raise THEME_EDITOR_ARCHIVE_LIMITS.maxFiles in code, but treat that as a last resort — the browser editor isn't meant for arbitrarily large archives.","Prefer uploading the theme via the Ghost Admin upload (server-side extraction) instead of the in-browser editor for large themes."],"exampleFix":"// before — extraction throws ThemeArchiveExtractionError mid-flow\nconst snapshot = await extractThemeArchive(arrayBuffer);\n\n// after — catch and branch on the typed reason\nimport {ThemeArchiveExtractionError} from './theme-editor-utils';\ntry {\n    const snapshot = await extractThemeArchive(arrayBuffer);\n} catch (e) {\n    if (e instanceof ThemeArchiveExtractionError && e.reason === 'too_many_files') {\n        setUploadError(e.message); // 'This theme archive contains too many files ...'\n        return;\n    }\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"import {ThemeArchiveExtractionError} from './theme-editor-utils';\nfunction isTooManyFiles(e: unknown): e is ThemeArchiveExtractionError {\n    return e instanceof ThemeArchiveExtractionError && e.reason === 'too_many_files';\n}","tryCatchPattern":"import {extractThemeArchive, ThemeArchiveExtractionError} from './theme-editor-utils';\ntry {\n    const snapshot = await extractThemeArchive(arrayBuffer);\n} catch (e) {\n    if (e instanceof ThemeArchiveExtractionError) {\n        setUploadError(e.message); // e.reason distinguishes too_many_files / too_large / invalid_archive\n        return;\n    }\n    throw e;\n}","preventionTips":["Pre-scan the archive's entry count locally (unzip -l) before uploading to the browser editor.","Exclude node_modules, .git, and generated asset folders when zipping a theme.","For large themes, prefer server-side theme upload over the in-browser editor.","Branch on ThemeArchiveExtractionError.reason to show a specific message."],"tags":["theme","archive","zip","validation","browser"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}