# Troubleshooting

When a serverless app can't render, the Crowdin host replaces it with a diagnostic panel. Each panel maps to a specific failure:

| Panel | What happened |
|-------|---------------|
| *This app has no bundle yet* | The app has no JavaScript bundle to run - nothing was published and no dev server is linked |
| *We couldn't load the app* | The bundle URL failed to load |
| *The app is taking too long to load* | The bundle didn't respond within a few seconds |
| *The app didn't start* | The bundle loaded but never registered the expected module |

## This app has no bundle yet

Run [`publish`](/serverless-apps/reference/cli-commands/#publish) to upload a bundle, or [`dev`](/serverless-apps/reference/cli-commands/#dev) and accept the switch to the external bundle mode so Crowdin loads the app from your machine.

## We couldn't load the app

When the bundle URL points at `localhost`, check that the dev server is actually running on that port. Two browser-specific traps:

- **Chrome** blocks requests to local addresses unless the page is allowed local network access - check the permission prompt or site settings.
- **Safari** refuses plain-HTTP localhost requests from an HTTPS page, and the CLI dev server is HTTP-only - publish the bundle instead, or develop in another browser (a local HTTPS reverse proxy in front of the dev server also works).

For a published bundle, reload the page; if it persists, the uploaded bundle is broken - re-run `publish`.

## The app is taking too long to load

The bundle URL is reachable but slow to respond. With a dev server, check it is running and reachable on that address (the first compile after startup can also take a moment - reload once it settles). For a published bundle, check your network connection and reload the page.

## The app didn't start

The bundle executed, but the host waited and no module matching `context.app.type` and `key` was registered. Typical causes:

- **Registration ran too late.** `prepare*` calls must run [synchronously at the top level of the bundle](/serverless-apps/building-app/modules/) - not inside an async callback, a dynamic import, or an event handler.
- **Key mismatch.** With several modules of one type in the manifest, each registration must pass its manifest `key`: `prepareProjectMenu({ … }, "my-key")`.
- **Module type not registered at all.** Every module type declared in the manifest must have a matching `prepare*` call in the bundle - one bundle serves them all.

The browser console shows what the SDK dispatcher resolved (`[apps-sdk] no module registered for …`).

## The app started, then the content area shows an error (or nothing)

Once a module registers, the host's diagnostic panels are out of the picture - anything that fails from here on is the app's own render path, and there is no host panel for it. The SDK reports these failures in two ways:

- **A throw during React rendering** is caught by the error boundary built into [`AppUiProvider`](/serverless-apps/building-app/user-interface/#render-errors): the app area shows the error message in an alert, and React logs the full error to the console.
- **A throw or rejection in the module's `render()` itself** - before React mounts anything - is reported by the dispatcher: `[apps-sdk] module render() failed` in the console, plus a plain-text message in the app area (unless the app already drew something).

Both messages appear in the console of the app's iframe, which is cross-origin to the hosting Crowdin page - in the browser DevTools, pick the app's frame in the console context selector if you don't see them.

## Opening the app directly in a browser

The bundle only runs inside the Crowdin host - opened directly (e.g. `http://localhost:8080/app.js` or your external URL), the SDK has no host bridge to talk to and throws. To see the app, open it inside Crowdin: [`preview`](/serverless-apps/reference/cli-commands/#preview) deep-links to the right page.

## Changes don't show up

- Each `dev` server carries its own hot reload on its own port, so several can run at once. When port 8080 is taken, `dev` reports the port it moved to (`Port 8080 is in use, using 8081 instead.`) and points Crowdin at that one. With `--port` the port is exact: `dev` stops instead of moving, so nothing silently takes over where Crowdin loads the app from.
- Translation catalogs are compiled once when `dev` starts - restart the dev server after editing `.po` files.
- The local `manifest.json` and Crowdin's copy can drift - `manifest status` shows the diff (and exits with code 1), `manifest push` / `manifest pull` [resolve it](/serverless-apps/development/dev-and-publish/#keeping-the-manifest-in-sync).

## The console shows Content Security Policy violations

The app iframe runs under a strict CSP. What it allows and forbids:

- **Allowed**: `fetch`/XHR/WebSocket requests to any HTTPS host (plus localhost during development) - calling third-party APIs directly is fine; images, media, and fonts from `https:`, `data:`, and `blob:` URLs.
- **Forbidden**: Web Workers (`worker-src 'none'` - breaks libraries that spawn workers, such as code editors or PDF renderers), nested iframes and embeds (`frame-src`/`child-src 'none'`), native form submissions (`form-action 'none'`), and scripts from anywhere but the app's bundle.

A library that fails only inside Crowdin most likely hits one of these - the console's CSP violation report names the blocked directive.

## API calls fail

- Every REST call must be covered by the [`scopes`](/serverless-apps/reference/manifest/#scopes) declared in the manifest - calls outside them are rejected, and an empty `scopes` array means no API access at all. Note that `:write` does not imply read access.
- `FormData` bodies and GraphQL are not supported by the host bridge - see the [API limitations](/serverless-apps/building-app/crowdin-api/#limitations).