RocketChat/Rocket.Chat · error
Failed to parse app.json
Error message
Failed to parse app.json: ${e instanceof Error ? e.message : String(e)} What it means
getAppManifest() throws this wrapper around JSON.parse(strFromU8(unzippedAppBuffer['app.json'])) when the manifest bytes are present but are not valid JSON — the underlying SyntaxError message is embedded in the thrown string. The manifest is then expected to conform to AppManifestSchema, so this guard catches malformed manifests before schema validation.
Solutions
- Run the manifest through a strict JSON validator (jq . app.json or JSON.parse in node) and fix the reported syntax error.
- Remove comments and trailing commas; ensure plain double-quoted JSON.
- Save as UTF-8 without BOM and rebuild the zip.
Example fix
// before (app.json — invalid JSON)
{
"id": "my-app",
"version": "0.1.0", // comments not allowed
}
// after
{
"id": "my-app",
"version": "0.1.0"
} Defensive patterns
Strategy: validation
Validate before calling
const raw = strFromU8(unzipped['app.json']).replace(/^\uFEFF/, ''); // strip BOM JSON.parse(raw); // throws early with a precise position if invalid // then validate against AppManifestSchema before install
Type guard
const isValidManifestJson = (raw: string): boolean => {
try {
JSON.parse(raw);
return true;
} catch {
return false;
}
}; Try / catch
try {
const manifest = getManifestFromZippedApp(file);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Failed to parse app.json')) {
// surface the embedded SyntaxError position to the app developer
dispatchToastMessage({ type: 'error', message: e.message });
return;
}
throw e;
} Prevention
- Lint app.json with a strict JSON parser (jq, node JSON.parse) in CI.
- No comments, no trailing commas, double quotes only, UTF-8 without BOM.
- Generate manifests from tooling instead of hand-editing.
When it happens
Trigger: app.json containing JSON5-style syntax (comments, trailing commas, single quotes), a UTF-8 BOM or wrong encoding breaking the first token, minification/templating artifacts, or a truncated download leaving the file cut off.
Common situations: Hand-edited manifests with comments or trailing commas; manifests generated by templating tools that leave placeholders; Windows editors saving with BOM; app packages assembled by scripts that concatenate files.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- app-addon-not-valid
- invalid-license
- Marketplace_Invalid_Apps_Engine_Version
- No app.json file found in the zip
- No_results_found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/1e66744c1dbb8b67.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/client/views/marketplace/lib/getManifestFromZippedApp.ts:28
fileReader.onload = (e): void => resolve(new Uint8Array((e.target as any).result as Uint8Array));
fileReader.onerror = (e): void => reject(e);
fileReader.readAsArrayBuffer(file);
});
}
function unzipAppBuffer(zippedAppBuffer: Uint8Array): Uint8ArrayObject {
return unzipSync(zippedAppBuffer);
}
function getAppManifest(unzippedAppBuffer: Uint8ArrayObject): AppManifestSchema {
if (!unzippedAppBuffer['app.json']) {
throw new Error('No app.json file found in the zip');
}
try {
return JSON.parse(strFromU8(unzippedAppBuffer['app.json']));
} catch (e) {
throw new Error(`Failed to parse app.json: ${e instanceof Error ? e.message : String(e)}`);
}
}
async function unzipZippedApp(zippedApp: File | Uint8Array): Promise<Uint8ArrayObject> {
try {
if (zippedApp instanceof File) {
zippedApp = await fileToUint8Array(zippedApp);
}
return unzipAppBuffer(zippedApp);
} catch (e) {
console.error(e);
throw e;
}
}
export async function getManifestFromZippedApp(zippedApp: File): Promise<AppManifestSchema> {
const unzippedBuffer = await unzipZippedApp(zippedApp);View on GitHub (pinned to b2c16d5842)