{"record":{"id":"a214cab059ffb48e","repo":"RocketChat/Rocket.Chat","slug":"app-metadata-download-failed","errorCode":null,"errorMessage":"App metadata download failed","messagePattern":"App metadata download failed","errorType":"exception","errorClass":null,"httpStatus":400,"severity":"error","filePath":"apps/meteor/ee/server/apps/communication/rest.ts","lineNumber":322,"sourceCode":"\t\t\t\t\t\t\t\t\t.fetch(`v2/apps/${this.bodyParams.appId}/download/${this.bodyParams.version}?token=${downloadToken}`, {\n\t\t\t\t\t\t\t\t\t\theaders,\n\t\t\t\t\t\t\t\t\t\t// SECURITY: user needs specific privileges to send this. Bypassing the SSRF check is okay for now.\n\t\t\t\t\t\t\t\t\t\tignoreSsrfValidation: true,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t.catch((cause) => {\n\t\t\t\t\t\t\t\t\t\tthrow new Error('App package download failed', { cause });\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tApps.getMarketplaceClient()\n\t\t\t\t\t\t\t\t\t.fetch(`v1/apps/${this.bodyParams.appId}?appVersion=${this.bodyParams.version}`, {\n\t\t\t\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t\t\t\tAuthorization: `Bearer ${marketplaceToken}`,\n\t\t\t\t\t\t\t\t\t\t\t...headers,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t// SECURITY: user needs specific privileges to send this. Bypassing the SSRF check is okay for now.\n\t\t\t\t\t\t\t\t\t\tignoreSsrfValidation: true,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t.catch((cause) => {\n\t\t\t\t\t\t\t\t\t\tthrow new Error('App metadata download failed', { cause });\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t]);\n\n\t\t\t\t\t\t\tif (downloadResponse.headers.get('content-type') !== 'application/zip') {\n\t\t\t\t\t\t\t\tthrow new Error('Invalid url. It doesn\\'t exist or is not \"application/zip\".');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbuff = Buffer.from(await downloadResponse.arrayBuffer());\n\t\t\t\t\t\t\tmarketplaceInfo = await marketplaceResponse.json();\n\n\t\t\t\t\t\t\t// Note: marketplace responds with an array of the marketplace info on the app, but it is expected\n\t\t\t\t\t\t\t// to always have one element since we are fetching a specific app version.\n\t\t\t\t\t\t\tif (!Array.isArray(marketplaceInfo) || marketplaceInfo?.length !== 1) {\n\t\t\t\t\t\t\t\torchestrator.getRocketChatLogger().error({ msg: 'Error getting app information from marketplace', marketplaceInfo });\n\t\t\t\t\t\t\t\tthrow new Error('Invalid response from the Marketplace');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tpermissionsGranted = this.bodyParams.permissionsGranted;","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/apps/communication/rest.ts#L304-L340","documentation":"Thrown during marketplace app installation when the second parallel request — fetching app metadata from v1/apps/:appId?appVersion=:version on the cloud marketplace — rejects. Like the package download (162) it is a transport-level wrapper: the original network error is preserved as { cause }. Only the download-vs-metadata branch differs; metadata failing means the marketplace never answered the info request even though install proceeded this far.","triggerScenarios":"POST to the marketplace-install route where the metadata fetch inside Promise.all rejects: network partition, 5xx that makes fetch throw through the client, timeout to marketplace.rocket.chat. The download branch may succeed; the whole install still aborts and the error is logged as 'Error installing app from marketplace:'.","commonSituations":"Flaky egress link that passes one request but drops the parallel one; cloud marketplace partial outage affecting only the v1 info endpoint; proxy rate-limiting concurrent connections; MTU/keepalive issues killing the second socket of Promise.all.","solutions":["Read err.cause in the server log to get the real network error (ENOTFOUND, ETIMEDOUT, certificate, etc.).","Stabilize egress: fix proxy/DNS/TLS config on the server host so parallel requests to marketplace.rocket.chat both succeed.","Retry the install; transient drops of one of the two parallel requests are common and a retry usually passes.","If persistent, test each endpoint directly from the host: curl 'https://marketplace.rocket.chat/v1/apps/<appId>?appVersion=<version>' to confirm which URL is failing."],"exampleFix":"// before\ntry {\n  await installFromMarketplace(appId, version);\n} catch (e) {\n  console.log(e.message); // \"App metadata download failed\" — real reason hidden\n}\n\n// after: log the cause chain to identify the network problem\ntry {\n  await installFromMarketplace(appId, version);\n} catch (e) {\n  console.error(e.message, { cause: e.cause }); // e.g. TypeError: fetch failed: ETIMEDOUT\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await installFromMarketplace(appId, version);\n} catch (e) {\n  if (e instanceof Error && e.message === 'App metadata download failed') {\n    const cause = (e.cause as Error)?.message ?? 'unknown';\n    // ETIMEDOUT/ENOTFOUND -> fix egress then retry; do not blind-retry more than twice\n  }\n  throw e;\n}","preventionTips":["Keep egress stable for parallel marketplace requests (proxy connection limits > 2).","Retry installs once or twice with backoff; persistent metadata failures indicate config, not luck.","Capture the cause chain in monitoring so the real network error is never lost."],"tags":["marketplace","network","app-install","fetch","cloud","enterprise"],"backgroundTag":"upstream-request-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}