{"record":{"id":"2aeaad073f65eea0","repo":"OrchardCMS/OrchardCore","slug":"config-url-is-missing-the-required-orchardbaseurl-field","errorCode":null,"errorMessage":"${CONFIG_URL} is missing the required \"orchardBaseUrl\" field.","messagePattern":"(.+?) is missing the required \"orchardBaseUrl\" field\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/OrchardCore.Modules/OrchardCore.Media/Assets/media-gallery/src/standalone.ts","lineNumber":38,"sourceCode":"\n/**\n * Entry point for the **standalone** media gallery — the app hosted on its own origin against a\n * remote OrchardCore tenant. Unlike the embedded entry (main.ts), there is no Razor host to inject\n * `<media-gallery>` attributes: config comes from a fetched `config.json`, and the App is rendered\n * with an already-resolved two-origin runtime config. Auth is bearer + interactive (handled inside\n * App.vue from the injected config).\n */\n\nconst CONFIG_URL = \"config.json\";\n\nasync function loadConfigSource(): Promise<IStandaloneConfigSource> {\n  const response = await fetch(CONFIG_URL, { cache: \"no-store\" });\n  if (!response.ok) {\n    throw new Error(`Failed to load ${CONFIG_URL}: ${response.status} ${response.statusText}`);\n  }\n  const source = (await response.json()) as IStandaloneConfigSource;\n  if (!source.orchardBaseUrl) {\n    throw new Error(`${CONFIG_URL} is missing the required \"orchardBaseUrl\" field.`);\n  }\n  return source;\n}\n\n/** Load the media gallery's UI labels from the remote Orchard tenant (the same \"media-gallery\" JS\n * localizations the embedded admin page renders, for the server's resolved culture). The endpoint is\n * anonymous, so this runs before authentication. Falls back to an empty set on failure. */\nasync function loadTranslations(apiBaseUrl: string): Promise<string> {\n  try {\n    // Default fetch caching: the endpoint serves Cache-Control/ETag, so repeat loads hit the\n    // browser cache or revalidate to a 304 instead of re-downloading the label set every boot.\n    const base = apiBaseUrl.endsWith(\"/\") ? apiBaseUrl : `${apiBaseUrl}/`;\n    const response = await fetch(`${base}api/media/localizations`);\n    if (response.ok) {\n      return JSON.stringify(await response.json());\n    }\n  } catch {\n    // Endpoint unreachable — fall back to empty (labels use their built-in fallbacks where present).","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore.Modules/OrchardCore.Media/Assets/media-gallery/src/standalone.ts#L20-L56","documentation":"After successfully fetching and parsing config.json, the standalone media gallery validates that the object contains a truthy orchardBaseUrl field. If missing or empty, it throws this Error. The base URL is required to call Orchard APIs (translations, SignalR, media endpoints) from the standalone host.","triggerScenarios":"config.json exists and returns 200 with valid JSON, but the JSON lacks orchardBaseUrl or has it set to an empty string/null.","commonSituations":"Hand-edited config.json missing the field; a config template copied without filling in the tenant URL; config generated by an older tool version that did not emit the field.","solutions":["Add a non-empty orchardBaseUrl value (the Orchard tenant root URL) to config.json and redeploy.","Validate config.json against IStandaloneConfigSource before deploying.","Check for trailing whitespace-only values; the check is truthiness-based so '' fails.","If the field name changed between versions, align the config schema with the bundle version you ship."],"exampleFix":"// before (config.json)\n{ \"signalrEnabled\": true }\n// after (config.json)\n{ \"orchardBaseUrl\": \"https://my-site.example.com\", \"signalrEnabled\": true }","handlingStrategy":"validation","validationCode":"// Validate config.json schema before shipping\nconst cfg = JSON.parse(fs.readFileSync('config.json', 'utf8'));\nif (!cfg.orchardBaseUrl) throw new Error('config.json must define orchardBaseUrl');","typeGuard":"function isStandaloneConfigSource(v: unknown): v is IStandaloneConfigSource {\n  return typeof v === 'object' && v !== null && 'orchardBaseUrl' in v && typeof (v as IStandaloneConfigSource).orchardBaseUrl === 'string' && (v as IStandaloneConfigSource).orchardBaseUrl.length > 0;\n}","tryCatchPattern":"try { const src = await loadConfigSource(); }\ncatch (err) { if (String(err).includes('orchardBaseUrl')) { /* fix config.json and redeploy */ } throw err; }","preventionTips":["Version-control a config.json template with orchardBaseUrl filled per environment.","Lint config.json in CI before deployment.","Keep the config schema in sync with the bundle version."],"tags":["javascript","configuration","validation","json"],"backgroundTag":"missing-required-config-field","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}