dotnet/runtime · error · Error

Loader configuration error: 'mainAssemblyName' is required.

Error message

Loader configuration error: 'mainAssemblyName' is required.

What it means

validateLoaderConfig() in loader/config.ts throws this when loaderConfig.mainAssemblyName is empty/undefined. The main assembly is the entry-point DLL that runMain/runMainAndExit will invoke, so the loader refuses to proceed without it. It is called by HostBuilder.create(), download(), runMain(), runMainAndExit() and once more inside createRuntime().

Source

Thrown at src/native/libs/Common/JavaScript/loader/config.ts:15

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import type { Assets, LoaderConfig, LoaderConfigInternal } from "./types";
import { browserVirtualAppBase } from "./per-module";

export const loaderConfig: LoaderConfigInternal = {};

export function getLoaderConfig(): LoaderConfig {
    return loaderConfig;
}

export function validateLoaderConfig(): void {
    if (!loaderConfig.mainAssemblyName) {
        throw new Error("Loader configuration error: 'mainAssemblyName' is required.");
    }
    if (!loaderConfig.resources || !loaderConfig.resources.coreAssembly || loaderConfig.resources.coreAssembly.length === 0) {
        throw new Error("Loader configuration error: 'resources.coreAssembly' is required and must contain at least one assembly.");
    }
}

export function mergeLoaderConfig(source: Partial<LoaderConfigInternal>): void {
    defaultConfig(loaderConfig);
    normalizeConfig(source);
    mergeConfigs(loaderConfig, source);
}

function mergeConfigs(target: LoaderConfigInternal, source: Partial<LoaderConfigInternal>): LoaderConfigInternal {
    // no need to merge the same object
    if (target === source || source === undefined || source === null) return target;

    // Merge collections: target values first, then source values appended/spread on top.
    mergeResources(target.resources!, source.resources!);

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Call builder.withMainAssembly('YourApp') (without the .dll extension) before create()/runMain().
  2. Alternatively pass it via withConfig({ mainAssemblyName: 'YourApp', resources: {...} }) with the rest of the config.
  3. Ensure the generated boot config JSON is being loaded and merged (mergeLoaderConfig) — if you replace the default config load, re-add mainAssemblyName.
  4. Confirm the value is not an empty string; the check is truthy, so '' fails validation.

Example fix

// before
const dotnet = await dotnet.create();

// after
const dotnet = await dotnet.withMainAssembly('MyApp').create();
Defensive patterns

Strategy: validation

Validate before calling

import { getLoaderConfig } from './loader/config';
const cfg = getLoaderConfig();
if (!cfg.mainAssemblyName) {
  throw new Error('Call builder.withMainAssembly(name) before create().');
}

Type guard

function hasMainAssembly(c: any): c is { mainAssemblyName: string } {
  return typeof c?.mainAssemblyName === 'string' && c.mainAssemblyName.length > 0;
}

Try / catch

try {
  await builder.create();
} catch (err) {
  if (/mainAssemblyName.*required/.test(err.message)) {
    builder.withMainAssembly('MyApp');
    await builder.create();
  } else throw err;
}

Prevention

When it happens

Trigger: Invoking dotnet.create()/download()/runMain() on a HostBuilder that never received mainAssemblyName — i.e. neither withMainAssembly(...) was called nor a config object containing mainAssemblyName was merged via withConfig(...).

Common situations: Publishing without a boot config (blazor.boot.json / dotnet.js config) that normally supplies mainAssemblyName; programmatically building the host in a custom harness and forgetting the WithMainAssembly step; the merged config object had mainAssemblyName set to an empty string.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/b6b5b6240743f12b. Report an issue: GitHub.