dotnet/runtime · error · Error

Invalid config, resources is not set

Error message

Invalid config, resources is not set

What it means

createRuntime() guards its first line: if loaderConfig.resources or resources.coreAssembly is missing/empty, it throws before doing any work. This is the runtime-side twin of error 107 (validateLoaderConfig) — run.ts does its own precondition check rather than relying solely on validateLoaderConfig because createRuntime can be reached directly (e.g. download() path).

Solutions

  1. Ensure withConfig(bootConfig) is awaited and the config has resources.coreAssembly populated before create()/download().
  2. If loading config asynchronously, await it before invoking the builder.
  3. Reproduce the same flow the generated dotnet.*.js uses — it always awaits the boot JSON.
  4. Cross-check with validateLoaderConfig() output; if that throws 107, fix the config first.

Example fix

// before
const runtime = await dotnet.create(); // resources not set yet

// after
const boot = await fetch('./_framework/dotnet.boot.config.json').then(r => r.json());
dotnet.withConfig(boot);
const runtime = await dotnet.create();
Defensive patterns

Strategy: validation

Validate before calling

function isResourcesConfigured(cfg: any): boolean {
  return !!cfg?.resources && Array.isArray(cfg.resources.coreAssembly) && cfg.resources.coreAssembly.length > 0;
}
if (!isResourcesConfigured(loaderConfig)) {
  throw new Error('Boot config not loaded yet');
}

Type guard

function isRuntimeReadyConfig(c: unknown): boolean {
  return !!c && typeof c === 'object' && Array.isArray((c as any).resources?.coreAssembly) && (c as any).resources.coreAssembly.length > 0;
}

Try / catch

try { await dotnet.create(); } catch (e) {
  if (/resources is not set/.test((e as Error).message)) {
    const boot = await fetch('./_framework/dotnet.boot.config.json').then(r => r.json());
    dotnet.withConfig(boot);
    await dotnet.create();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling createRuntime before mergeLoaderConfig populated resources. Loading a config JSON that had no resources block. A bootstrap script that calls run before the boot config fetch resolved. A custom loader entry that skips withConfig.

Common situations: Race condition: code calls dotnet.create() before the fetch for dotnet.boot.config.json completes. Manually crafted config that omits resources. Mixing versions where an old bootstrap feeds a new loader.

Related errors


AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10). Data as JSON: /api/errors/677eaf5b86b99733. Report an issue: GitHub.

Appendix: source

Thrown at src/native/libs/Common/JavaScript/loader/run.ts:27

import { getIcuResourceName } from "./icu";
import { loaderConfig, validateLoaderConfig } from "./config";
import { fetchAssembly, fetchIcu, fetchNativeSymbols, fetchPdb, fetchSatelliteAssemblies, fetchVfs, fetchMainWasm, loadDotnetModule, loadJSModule, nativeModulePromiseController, verifyAllAssetsDownloaded, callLibraryInitializerOnRuntimeReady, callLibraryInitializerOnRuntimeConfigLoaded, prefetchAllResources, prefetchJSModuleLinks, resolveAllDownloadsQueued } from "./assets";
import { initPolyfillsLoader } from "./polyfills";
import { validateEngineFeatures } from "./bootstrap";

const runMainPromiseController = createPromiseCompletionSource<number>();

type DownloadMode = "none" | "cacheOnly" | "intoMemory";
let downloadMode: DownloadMode = "none";
let downloadDeferred: PromiseCompletionSource<void> | undefined;
let downloadedIntoMemory = false;
let configInitialized = false;
let modulesAfterConfigLoadedCache: [JsAsset, Promise<any>][] = [];

// many things happen in parallel here, but order matters for performance!
// ideally we want to utilize network and CPU at the same time
export async function createRuntime(downloadOnly: boolean, httpCacheOnly: boolean = false): Promise<any> {
    if (!loaderConfig.resources || !loaderConfig.resources.coreAssembly || !loaderConfig.resources.coreAssembly.length) throw new Error("Invalid config, resources is not set");
    try {
        runtimeState.creatingRuntime = true;

        // Re-entrancy guard: await any in-flight download, skip if already at requested level
        if (downloadOnly) {
            if (downloadDeferred) {
                await downloadDeferred.promise;
            }
            if (downloadMode === "intoMemory" || (httpCacheOnly && downloadMode === "cacheOnly")) {
                return;
            }
            downloadDeferred = createPromiseCompletionSource<void>();
        }

        // Fast path: download() already loaded everything into memory, create() just needs to init
        if (downloadedIntoMemory && !downloadOnly) {
            Module.runtimeKeepalivePush();
            await initializeCoreCLR();

View on GitHub (pinned to 60108ba66e)