{"record":{"id":"256496a6f9f8f206","repo":"TryGhost/Ghost","slug":"theme-is-not-compatible-or-contains-errors","errorCode":null,"errorMessage":"Theme is not compatible or contains errors.","messagePattern":"Theme is not compatible or contains errors\\.","errorType":"validation","errorClass":"ThemeValidationError","httpStatus":null,"severity":"error","filePath":"apps/admin-x-framework/src/utils/api/handle-response.ts","lineNumber":33,"sourceCode":"        }\n        throw new UnauthorizedError(response, await response.text());\n    } else if (!response.ok) {\n        if (!response.headers.get('content-type')?.includes('json')) {\n            throw new APIError(response, await response.text());\n        }\n\n        const data = await response.json() as ErrorResponse;\n\n        if (response.status === 403 && data.errors?.[0]?.message === 'Authorization failed') {\n            throw new UnauthorizedError(response, data);\n        } else if (data.errors?.[0]?.type === 'VersionMismatchError') {\n            throw new VersionMismatchError(response, data);\n        } else if (data.errors?.[0]?.type === 'ValidationError') {\n            throw new ValidationError(response, data);\n        } else if (data.errors?.[0]?.type === 'NoPermissionError') {\n            throw new ValidationError(response, data);\n        } else if (data.errors?.[0]?.type === 'ThemeValidationError') {\n            throw new ThemeValidationError(response, data);\n        } else if (data.errors?.[0]?.type === 'HostLimitError') {\n            throw new HostLimitError(response, data);\n        } else if (data.errors?.[0]?.type === 'EmailError') {\n            throw new EmailError(response, data);\n        } else {\n            throw new JSONError(response, data);\n        }\n    } else if (response.status === 204) {\n        return;\n    } else if (response.headers.get('content-type')?.includes('text/csv')) {\n        return await response.text();\n    } else {\n        return await response.json();\n    }\n};\n\nexport default handleResponse;\n","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/admin-x-framework/src/utils/api/handle-response.ts#L15-L51","documentation":"ThemeValidationError, thrown by handleResponse when errors[0].type === 'ThemeValidationError'. It signals that a theme being uploaded or activated failed Ghost's theme validation (GScan): missing required templates, invalid package.json, deprecated helpers, or structural defects. The detailed GScan report is in error.data.","triggerScenarios":"Uploading a .zip theme whose contents fail GScan, or activating a theme whose package.json/templates are invalid. Ghost returns the validation report as errors data.","commonSituations":"Custom theme missing required files (e.g., post.hbs); package.json with invalid/capabilities/engines fields; theme built for a newer Ghost API version; deprecated template usage flagged as errors.","solutions":["Inspect error.data.errors for the GScan report — it lists each failed check with file/line context.","Run GScan locally (npx gscan <theme>) and fix all errors before re-uploading.","Correct package.json (version, engines.ghost, api.compatibility) and ensure required templates exist."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Validate the theme zip locally before upload (run GScan).\n// npx gscan ./my-theme  → exits non-zero on errors.","typeGuard":"import {ThemeValidationError} from '@tryghost/admin-x-framework/utils/errors';\nfunction isThemeValidationError(e: unknown): e is ThemeValidationError {\n    return e instanceof ThemeValidationError;\n}","tryCatchPattern":"import {ThemeValidationError} from '@tryghost/admin-x-framework/utils/errors';\ntry {\n    await api.themes.upload(file);\n} catch (e) {\n    if (e instanceof ThemeValidationError) {\n        // data.errors carries the GScan report\n        showThemeErrors(e.data?.errors ?? []);\n    } else throw e;\n}","preventionTips":["Run Gscan locally (npx gscan <theme>) and resolve all errors before uploading.","Keep package.json (engines.ghost, api.compatibility) in sync with the target Ghost version.","Ensure required templates (default.hbs, post.hbs, page.hbs, etc.) exist in the zip."],"tags":["theme","validation","gscan","upload","activation"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}