RocketChat/Rocket.Chat · error
App package download failed
Error message
App package download failed
What it means
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.
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.
Example fix
// before
await API.call('POST', 'apps/1fa9dc27-d0b2-4b34-b6d7-35564e569d3d/install', { appId, version });
// fails: "App package download failed" (cause: fetch failed: ENOTFOUND marketplace.rocket.chat)
// after: configure egress proxy, then retry
// Admin -> Settings -> General -> Outbound Proxy (or env HTTP_PROXY/HTTPS_PROXY)
await API.call('POST', 'apps/1fa9dc27-d0b2-4b34-b6d7-35564e569d3d/install', { appId, version }); Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight reachability check before attempting install
import { fetch } from 'undici';
async function marketplaceReachable(): Promise<boolean> {
try {
const r = await fetch('https://marketplace.rocket.chat', { method: 'HEAD' });
return r.status < 500;
} catch {
return false;
}
} Try / catch
for (let attempt = 1; attempt <= 3; attempt++) {
try {
return await installFromMarketplace(appId, version);
} catch (e) {
if (e instanceof Error && e.message === 'App package download failed' && attempt < 3) {
await sleep(2 ** attempt * 500); // inspect e.cause for DNS/TLS specifics
continue;
}
throw e;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- App metadata download failed
- Invalid response from the Marketplace
- Invalid url. It doesn't exist or is not "application/zip".
- Marketplace_Failed_To_Fetch_Apps
- Marketplace_Failed_To_Fetch_Categories
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/3af4cf13b5cf4415.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/ee/server/apps/communication/rest.ts:310
} catch (err: any) {
orchestrator.getRocketChatLogger().error({ msg: 'Error fetching App from URL:', err });
return API.v1.internalError();
}
} else if ('appId' in this.bodyParams && this.bodyParams.appId && this.bodyParams.marketplace && this.bodyParams.version) {
const headers = getDefaultHeaders();
try {
const downloadToken = await getWorkspaceAccessToken(true, 'marketplace:download', false);
const marketplaceToken = await getWorkspaceAccessToken();
const [downloadResponse, marketplaceResponse] = await Promise.all([
Apps.getMarketplaceClient()
.fetch(`v2/apps/${this.bodyParams.appId}/download/${this.bodyParams.version}?token=${downloadToken}`, {
headers,
// SECURITY: user needs specific privileges to send this. Bypassing the SSRF check is okay for now.
ignoreSsrfValidation: true,
})
.catch((cause) => {
throw new Error('App package download failed', { cause });
}),
Apps.getMarketplaceClient()
.fetch(`v1/apps/${this.bodyParams.appId}?appVersion=${this.bodyParams.version}`, {
headers: {
Authorization: `Bearer ${marketplaceToken}`,
...headers,
},
// SECURITY: user needs specific privileges to send this. Bypassing the SSRF check is okay for now.
ignoreSsrfValidation: true,
})
.catch((cause) => {
throw new Error('App metadata download failed', { cause });
}),
]);
if (downloadResponse.headers.get('content-type') !== 'application/zip') {
throw new Error('Invalid url. It doesn\'t exist or is not "application/zip".');
}View on GitHub (pinned to b2c16d5842)