{"record":{"id":"51544c0f3909136d","repo":"laurent22/joplin","slug":"screenshottype-is-not-a-valid-screenshot-type","errorCode":null,"errorMessage":"${screenshotType} is not a valid screenshot type. Valid types are: \n${allPossibleScreenshotsType}\n","messagePattern":"(.+?) is not a valid screenshot type\\. Valid types are: \n(.+?)\n","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/generator-joplin/generators/app/templates/webpack.config.js","lineNumber":111,"sourceCode":"\tif ((categories.length !== new Set(categories).size)) throw new Error('Repeated categories are not allowed');\n\t// eslint-disable-next-line github/array-foreach -- Old code before rule was applied\n\tcategories.forEach(category => {\n\t\tif (!allPossibleCategories.map(category => { return category.name; }).includes(category)) throw new Error(`${category} is not a valid category. Please make sure that the category name is lowercase. Valid categories are: \\n${allPossibleCategories.map(category => { return category.name; })}\\n`);\n\t});\n}\n\nfunction validateScreenshots(screenshots) {\n\tif (!screenshots) return null;\n\tfor (const screenshot of screenshots) {\n\t\tif (!screenshot.src) throw new Error('You must specify a src for each screenshot');\n\n\t\t// Avoid attempting to download and verify URL screenshots.\n\t\tif (screenshot.src.startsWith('https://') || screenshot.src.startsWith('http://')) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst screenshotType = screenshot.src.split('.').pop();\n\t\tif (!allPossibleScreenshotsType.includes(screenshotType)) throw new Error(`${screenshotType} is not a valid screenshot type. Valid types are: \\n${allPossibleScreenshotsType}\\n`);\n\n\t\tconst screenshotPath = path.resolve(rootDir, screenshot.src);\n\n\t\t// Max file size is 1MB\n\t\tconst fileMaxSize = 1024;\n\t\tconst fileSize = fs.statSync(screenshotPath).size / 1024;\n\t\tif (fileSize > fileMaxSize) throw new Error(`Max screenshot file size is ${fileMaxSize}KB. ${screenshotPath} is ${fileSize}KB`);\n\t}\n}\n\nfunction readManifest(manifestPath) {\n\tconst content = fs.readFileSync(manifestPath, 'utf8');\n\tconst output = JSON.parse(content);\n\tif (!output.id) throw new Error(`Manifest plugin ID is not set in ${manifestPath}`);\n\tvalidateCategories(output.categories);\n\tvalidateScreenshots(output.screenshots);\n\treturn output;\n}","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/generator-joplin/generators/app/templates/webpack.config.js#L93-L129","documentation":"Thrown by validateScreenshots() after extracting the file extension via `screenshot.src.split('.').pop()`. The extension must be in allPossibleScreenshotsType. Only local files reach this check — https:// and http:// src values `continue` early and bypass it.","triggerScenarios":"A local screenshot src has an extension that is not in allPossibleScreenshotsType (e.g. .bmp, .tiff, .webp if unsupported), or src has no dot so split('.').pop() returns the whole filename.","commonSituations":"Author exports a screenshot in an uncommon format; file has no extension; case mismatch where the list expects lowercase but the file uses uppercase (.PNG).","solutions":["Convert the screenshot to a supported type — the valid list is printed in the error message.","Rename the file so its extension is lowercase and matches an entry in allPossibleScreenshotsType.","If the file genuinely has no extension, add the correct one.","Re-run the build."],"exampleFix":"// before\n\"src\": \"screenshots/hero.BMP\"\n// after (convert + rename)\n\"src\": \"screenshots/hero.png\"","handlingStrategy":"validation","validationCode":"const supported = new Set(allPossibleScreenshotsType); // exported list\nconst bad = (manifest.screenshots || [])\n  .filter(s => s.src && !/^https?:\\/\\//.test(s.src))\n  .filter(s => !supported.has(s.src.split('.').pop().toLowerCase()));\nif (bad.length) throw new Error(`Unsupported screenshot type for: ${bad.map(s => s.src).join(', ')}`);","typeGuard":"const isSupportedScreenshotType = (src, supported) => /^https?:\\/\\//.test(src) || supported.has(src.split('.').pop().toLowerCase());","tryCatchPattern":"try { validateScreenshots(manifest.screenshots); }\ncatch (e) { if (/not a valid screenshot type/.test(e.message)) { /* convert or replace */ } else throw e; }","preventionTips":["Normalise screenshot filenames to a single supported extension when adding them to the repo.","Lowercase extensions on commit via a pre-commit hook to avoid case mismatch with the supported list."],"tags":["plugin-build","manifest-validation","generator-joplin"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}