{"record":{"id":"3af4cf13b5cf4415","repo":"RocketChat/Rocket.Chat","slug":"app-package-download-failed","errorCode":null,"errorMessage":"App package download failed","messagePattern":"App package download failed","errorType":"exception","errorClass":null,"httpStatus":400,"severity":"error","filePath":"apps/meteor/ee/server/apps/communication/rest.ts","lineNumber":310,"sourceCode":"\t\t\t\t\t\t} catch (err: any) {\n\t\t\t\t\t\t\torchestrator.getRocketChatLogger().error({ msg: 'Error fetching App from URL:', err });\n\t\t\t\t\t\t\treturn API.v1.internalError();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if ('appId' in this.bodyParams && this.bodyParams.appId && this.bodyParams.marketplace && this.bodyParams.version) {\n\t\t\t\t\t\tconst headers = getDefaultHeaders();\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst downloadToken = await getWorkspaceAccessToken(true, 'marketplace:download', false);\n\t\t\t\t\t\t\tconst marketplaceToken = await getWorkspaceAccessToken();\n\n\t\t\t\t\t\t\tconst [downloadResponse, marketplaceResponse] = await Promise.all([\n\t\t\t\t\t\t\t\tApps.getMarketplaceClient()\n\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}","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/apps/communication/rest.ts#L292-L328","documentation":"Thrown while installing an app from the marketplace (EE apps REST endpoint) when the HTTP request that downloads the app's zip package fails at the network/transport level. The fetch to v2/apps/:appId/download/:version on the Rocket.Chat cloud marketplace is wrapped so any rejection (DNS failure, timeout, TLS error, reset connection) is rethrown as this Error with the original cause attached via { cause }. The message means the package bytes never arrived, not that the app is invalid.","triggerScenarios":"POST to the marketplace-install route with bodyParams.appId/version where Promise.all rejects on the download branch: egress firewall blocking marketplace.rocket.chat, proxy misconfiguration, cloud outage, or an aborted connection. Note the sibling metadata fetch can also fail (163); whichever rejects first surfaces. The route runs with ignoreSsrfValidation: true, so SSRF blocking is not the cause here.","commonSituations":"Air-gapped or corporate networks without an HTTPS proxy configured for the server (check outbound settings in Administration); transient cloud marketplace outage during install; IPv6-only misrouting; self-signed SSL inspection proxy breaking TLS; workspace registered against a cloud region that is unreachable.","solutions":["Inspect err.cause (the original fetch error) in the server log line 'Error installing app from marketplace:' to identify DNS/TLS/timeout specifics.","Verify outbound connectivity from the server host: curl -I https://marketplace.rocket.chat and configure the HTTP proxy egress settings if a corporate proxy is required.","Retry the install after transient cloud incidents; check https://status.rocket.chat.","If TLS interception is in play, add the corporate CA to the server trust store (NODE_EXTRA_CA_CERTS) or bypass the inspection proxy for marketplace domains."],"exampleFix":"// before\nawait API.call('POST', 'apps/1fa9dc27-d0b2-4b34-b6d7-35564e569d3d/install', { appId, version });\n// fails: \"App package download failed\" (cause: fetch failed: ENOTFOUND marketplace.rocket.chat)\n\n// after: configure egress proxy, then retry\n// Admin -> Settings -> General -> Outbound Proxy (or env HTTP_PROXY/HTTPS_PROXY)\nawait API.call('POST', 'apps/1fa9dc27-d0b2-4b34-b6d7-35564e569d3d/install', { appId, version });","handlingStrategy":"retry","validationCode":"// Pre-flight reachability check before attempting install\nimport { fetch } from 'undici';\nasync function marketplaceReachable(): Promise<boolean> {\n  try {\n    const r = await fetch('https://marketplace.rocket.chat', { method: 'HEAD' });\n    return r.status < 500;\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"for (let attempt = 1; attempt <= 3; attempt++) {\n  try {\n    return await installFromMarketplace(appId, version);\n  } catch (e) {\n    if (e instanceof Error && e.message === 'App package download failed' && attempt < 3) {\n      await sleep(2 ** attempt * 500); // inspect e.cause for DNS/TLS specifics\n      continue;\n    }\n    throw e;\n  }\n}","preventionTips":["Configure outbound proxy settings for the server host before enabling marketplace installs.","Monitor egress to marketplace.rocket.chat from the same network path as the app process.","Log e.cause, not just the wrapper message, so network root causes are visible."],"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"}