{"record":{"id":"5eb1328467ff899f","repo":"BookStackApp/BookStack","slug":"module-in-folder-foldername-is-missing-a-vali","errorCode":null,"errorMessage":"Module in folder \"{$folderName}\" is missing a valid 'name' property","messagePattern":"Module in folder \"(.+?)\" is missing a valid 'name' property","errorType":"exception","errorClass":"ThemeModuleException","httpStatus":null,"severity":"error","filePath":"app/Theming/ThemeModule.php","lineNumber":23,"sourceCode":"readonly class ThemeModule\n{\n    public function __construct(\n        public string $name,\n        public string $description,\n        public string $version,\n        public string $folderName,\n    ) {\n    }\n\n    /**\n     * Create a ThemeModule instance from JSON data.\n     *\n     * @throws ThemeModuleException\n     */\n    public static function fromJson(array $data, string $folderName): self\n    {\n        if (empty($data['name']) || !is_string($data['name'])) {\n            throw new ThemeModuleException(\"Module in folder \\\"{$folderName}\\\" is missing a valid 'name' property\");\n        }\n\n        if (!isset($data['description']) || !is_string($data['description'])) {\n            throw new ThemeModuleException(\"Module in folder \\\"{$folderName}\\\" is missing a valid 'description' property\");\n        }\n\n        if (!isset($data['version']) || !is_string($data['version'])) {\n            throw new ThemeModuleException(\"Module in folder \\\"{$folderName}\\\" is missing a valid 'version' property\");\n        }\n\n        if (!preg_match('/^v?\\d+\\.\\d+\\.\\d+(-.*)?$/', $data['version'])) {\n            throw new ThemeModuleException(\"Module in folder \\\"{$folderName}\\\" has an invalid 'version' format. Expected semantic version format like '1.0.0' or 'v1.0.0'\");\n        }\n\n        return new self(\n            name: $data['name'],\n            description: $data['description'],\n            version: $data['version'],","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Theming/ThemeModule.php#L5-L41","documentation":"ThemeModule::fromJson validates a module's JSON descriptor (module.php/meta file) and throws ThemeModuleException when the 'name' key is absent, empty, or not a string. The folder name is included in the message to identify the offending module.","triggerScenarios":"Loading theme modules via loadFromFolder or getModuleInstance where the module's JSON/file returns an array without a valid string 'name' property — e.g. missing key, null, or an empty string.","commonSituations":"Hand-written module.json files with a typo ('Name' instead of 'name'); modules copied from older themes predating the descriptor format; empty or corrupted descriptor files; packaging the wrong file so $data is empty.","solutions":["Open the module folder named in the message and add/fix the 'name' key as a non-empty string in its descriptor","Verify you're editing the exact file BookStack parses (check ThemeModule loader conventions, e.g. module.php returning an array)","Re-download/repackage the module from a trusted source if the file is corrupted","Check for case-sensitivity typos in the JSON keys"],"exampleFix":"// before (module descriptor)\n{ \"description\": \"My module\" }\n// after\n{ \"name\": \"my-module\", \"description\": \"My module\" }","handlingStrategy":"validation","validationCode":"function validateModuleDescriptor(array $data, string $folder): void {\n    if (empty($data['name']) || !is_string($data['name'])) {\n        throw new InvalidArgumentException(\"Module in folder \\\"{$folder}\\\" needs a non-empty string 'name'\");\n    }\n}","typeGuard":"function hasValidName(array $data): bool {\n    return isset($data['name']) && is_string($data['name']) && $data['name'] !== '';\n}","tryCatchPattern":"try {\n    $module = ThemeModule::fromJson($data, $folderName);\n} catch (ThemeModuleException $e) {\n    Log::warning('Invalid theme module', ['folder' => $folderName, 'error' => $e->getMessage()]);\n    // skip module or fail upload with clear message\n}","preventionTips":["Include name, description, and version in every module descriptor","Use lowercase kebab-case keys exactly as documented (JSON is case-sensitive)","Lint descriptor JSON before packaging a module ZIP","Test module loading locally before distributing"],"tags":["theming","validation","json","bookstack"],"backgroundTag":"missing-required-argument","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}