OrchardCMS/OrchardCore · error · Error

Failed to load

Error message

Failed to load ${CONFIG_URL}: ${response.status} ${response.statusText}

What it means

The standalone media gallery boots by fetching config.json (CONFIG_URL) relative to the script location with cache: no-store. If the HTTP response is not ok, it throws an Error containing the status code and status text. This is a bootstrap guard: without config the Vue app cannot know the Orchard base URL or runtime options.

Solutions

  1. Ensure config.json is deployed next to the standalone bundle at the URL the page fetches.
  2. Check the browser Network tab for the actual failing URL and status; fix the hosting path or permissions.
  3. Verify static files (including .json) are served by the host (web server config, SPA fallback rules).
  4. Handle the rejection in bootstrap code and show a user-friendly message pointing at the missing config.

Example fix

// before
throw new Error(`Failed to load ${CONFIG_URL}: ${response.status} ${response.statusText}`);
// after
if (!response.ok) {
  console.error(`config.json fetch failed: ${response.status}; ensure config.json is deployed next to the bundle`);
  return fallbackConfig;
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-flight check the config endpoint
const res = await fetch('config.json', { cache: 'no-store' });
if (!res.ok) console.warn(`config.json unavailable: ${res.status}; check deployment`);

Try / catch

try { const src = await loadConfigSource(); }
catch (err) {
  document.body.innerHTML = 'Media gallery failed to load configuration. Ensure config.json is deployed.';
  throw err;
}

Prevention

When it happens

Trigger: Opening the standalone media gallery page when config.json is absent from the served location, returns 404/500, or the static file middleware/server fails to serve it.

Common situations: Deploying only the JS bundle without the accompanying config.json; wrong base path in hosting (config.json resolved relative to the app root); server returning 401/403 due to auth on static files; CDN misconfiguration.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/4bb62b7d5b9caeba. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore.Modules/OrchardCore.Media/Assets/media-gallery/src/standalone.ts:34

  setRuntimeConfig,
  type IStandaloneConfigSource,
  type IMediaRuntimeConfig,
} from "./services/RuntimeConfig";

/**
 * Entry point for the **standalone** media gallery — the app hosted on its own origin against a
 * remote OrchardCore tenant. Unlike the embedded entry (main.ts), there is no Razor host to inject
 * `<media-gallery>` attributes: config comes from a fetched `config.json`, and the App is rendered
 * with an already-resolved two-origin runtime config. Auth is bearer + interactive (handled inside
 * App.vue from the injected config).
 */

const CONFIG_URL = "config.json";

async function loadConfigSource(): Promise<IStandaloneConfigSource> {
  const response = await fetch(CONFIG_URL, { cache: "no-store" });
  if (!response.ok) {
    throw new Error(`Failed to load ${CONFIG_URL}: ${response.status} ${response.statusText}`);
  }
  const source = (await response.json()) as IStandaloneConfigSource;
  if (!source.orchardBaseUrl) {
    throw new Error(`${CONFIG_URL} is missing the required "orchardBaseUrl" field.`);
  }
  return source;
}

/** Load the media gallery's UI labels from the remote Orchard tenant (the same "media-gallery" JS
 * localizations the embedded admin page renders, for the server's resolved culture). The endpoint is
 * anonymous, so this runs before authentication. Falls back to an empty set on failure. */
async function loadTranslations(apiBaseUrl: string): Promise<string> {
  try {
    // Default fetch caching: the endpoint serves Cache-Control/ETag, so repeat loads hit the
    // browser cache or revalidate to a 304 instead of re-downloading the label set every boot.
    const base = apiBaseUrl.endsWith("/") ? apiBaseUrl : `${apiBaseUrl}/`;
    const response = await fetch(`${base}api/media/localizations`);
    if (response.ok) {

View on GitHub (pinned to 4306c0717f)