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

  1. Inspect err.cause (the original fetch error) in the server log line 'Error installing app from marketplace:' to identify DNS/TLS/timeout specifics.
  2. 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.
  3. Retry the install after transient cloud incidents; check https://status.rocket.chat.
  4. 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

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


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)