laurent22/joplin · error · Error

You must specify a src for each screenshot

Error message

You must specify a src for each screenshot

What it means

Thrown by validateScreenshots() when iterating the manifest's `screenshots` array. Every screenshot object must have a `src` property pointing at the image file (or URL). Missing src is treated as a malformed manifest and aborts the build before any further checks (type, size) run.

Source

Thrown at packages/generator-joplin/generators/app/templates/webpack.config.js:103

		console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
		console.info(chalk.cyan('Git information will not be stored in plugin info file'));
		return '';
	}
}

function validateCategories(categories) {
	if (!categories) return null;
	if ((categories.length !== new Set(categories).size)) throw new Error('Repeated categories are not allowed');
	// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
	categories.forEach(category => {
		if (!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`);
	});
}

function validateScreenshots(screenshots) {
	if (!screenshots) return null;
	for (const screenshot of screenshots) {
		if (!screenshot.src) throw new Error('You must specify a src for each screenshot');

		// Avoid attempting to download and verify URL screenshots.
		if (screenshot.src.startsWith('https://') || screenshot.src.startsWith('http://')) {
			continue;
		}

		const screenshotType = screenshot.src.split('.').pop();
		if (!allPossibleScreenshotsType.includes(screenshotType)) throw new Error(`${screenshotType} is not a valid screenshot type. Valid types are: \n${allPossibleScreenshotsType}\n`);

		const screenshotPath = path.resolve(rootDir, screenshot.src);

		// Max file size is 1MB
		const fileMaxSize = 1024;
		const fileSize = fs.statSync(screenshotPath).size / 1024;
		if (fileSize > fileMaxSize) throw new Error(`Max screenshot file size is ${fileMaxSize}KB. ${screenshotPath} is ${fileSize}KB`);
	}
}

View on GitHub (pinned to 2654b33620)

Solutions

  1. Open manifest.json and add a `src` field to every object in the `screenshots` array.
  2. Verify the key is exactly `src` (not `source`, `path`, `url`).
  3. For local files confirm the path is relative to the project root and the file exists; for remote images use a full https:// or http:// URL (those skip local checks downstream).
  4. Re-run the build.

Example fix

// before (manifest.json)
"screenshots": [{ "caption": "Main view" }]
// after
"screenshots": [{ "src": "screenshots/main.png", "caption": "Main view" }]
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check every screenshot has a non-empty src.
const manifest = require('./manifest.json');
for (const s of manifest.screenshots || []) {
  if (!s.src || typeof s.src !== 'string') throw new Error(`Screenshot missing src: ${JSON.stringify(s)}`);
}

Type guard

const hasSrc = (s) => !!s && typeof s.src === 'string' && s.src.length > 0;

Try / catch

try { validateScreenshots(manifest.screenshots); }
catch (e) { if (/specify a src/.test(e.message)) { /* prompt author for missing src */ } else throw e; }

Prevention

When it happens

Trigger: readManifest() -> validateScreenshots(output.screenshots) -> for a screenshot object, `!screenshot.src` is truthy (undefined, empty string, or null).

Common situations: Author adds a screenshot entry with only a `caption` or wrong key name (e.g. 'path' instead of 'src'); copies a template entry and forgets to fill in the path; manifest.json was hand-edited and the key was accidentally removed.

Related errors


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/e05775e8779803f9. Report an issue: GitHub.