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

  1. Run the manifest through a strict JSON validator (jq . app.json or JSON.parse in node) and fix the reported syntax error.
  2. Remove comments and trailing commas; ensure plain double-quoted JSON.
  3. 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

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

Related errors


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)