dotnet/runtime · error · Error

Expected environment variable '${k}' to be a string but it w

Error message

Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'

What it means

Thrown by start_runtime while iterating config.environmentVariables: each value must be a string for mono_wasm_setenv, and any non-string value (number, boolean, object, null) is rejected with a message naming the offending key, its typeof, and value. This guards the environmentVariables dictionary before it is forwarded to native setenv.

Source

Thrown at src/mono/browser/runtime/startup.ts:442

    const simd = loaderHelpers.simd();
    const relaxedSimd = loaderHelpers.relaxedSimd();
    const exceptions = loaderHelpers.exceptionsFinal();
    runtimeHelpers.featureWasmSimd = await simd;
    runtimeHelpers.featureWasmRelaxedSimd = await relaxedSimd;
    runtimeHelpers.featureWasmFinalEh = await exceptions;
}

export async function start_runtime () {
    try {
        const mark = startMeasure();
        const environmentVariables = runtimeHelpers.config.environmentVariables || {};
        mono_log_debug("Initializing mono runtime");
        for (const k in environmentVariables) {
            const v = environmentVariables![k];
            if (typeof (v) === "string")
                mono_wasm_setenv(k, v);
            else
                throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`);
        }
        if (runtimeHelpers.config.runtimeOptions)
            mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions);

        if (runtimeHelpers.emscriptenBuildOptions.enableEventPipe) {
            const diagnosticPorts = "DOTNET_DiagnosticPorts";
            // connect JS client by default
            const jsReady = "js://ready";
            if (!environmentVariables[diagnosticPorts]) {
                environmentVariables[diagnosticPorts] = jsReady;
                mono_wasm_setenv(diagnosticPorts, jsReady);
            }
        } else if (runtimeHelpers.emscriptenBuildOptions.enableAotProfiler) {
            mono_wasm_init_aot_profiler(runtimeHelpers.config.aotProfilerOptions || {});
        } else if (runtimeHelpers.emscriptenBuildOptions.enableDevToolsProfiler) {
            mono_wasm_init_devtools_profiler();
        } else if (runtimeHelpers.emscriptenBuildOptions.enableLogProfiler) {
            mono_wasm_init_log_profiler(runtimeHelpers.config.logProfilerOptions || {});

View on GitHub (pinned to 60108ba66e)

Solutions

  1. Ensure every value in environmentVariables is a string: coerce at config-build time, e.g. Object.fromEntries(Object.entries(env).map(([k,v]) => [k, String(v)])).
  2. Validate the config object's environmentVariables field as Record<string,string> before start_runtime runs.
  3. Fix the upstream source of the config (file, URL) to emit string values.

Example fix

// before
config.environmentVariables = { DOTNET_gcServer: 1 };
// after
config.environmentVariables = { DOTNET_gcServer: '1' };
Defensive patterns

Strategy: validation

Validate before calling

function coerceEnvVars(env: Record<string, unknown>): Record<string, string> {
  const out: Record<string, string> = {};
  for (const [k, v] of Object.entries(env)) {
    if (typeof v !== 'string') throw new Error(`environment variable '${k}' must be a string, got ${typeof v}`);
    out[k] = v;
  }
  return out;
}
config.environmentVariables = coerceEnvVars(config.environmentVariables);

Type guard

function isStringRecord(o: unknown): o is Record<string, string> {
  return !!o && typeof o === 'object' && Object.values(o).every(v => typeof v === 'string');
}

Prevention

When it happens

Trigger: Providing runtimeHelpers.config.environmentVariables = { SOME_KEY: 123 } or { DEBUG: true } - any value that is not typeof 'string'. The check on line 439 fails and line 442 throws.

Common situations: Loading runtime config from JSON where numbers/booleans are not coerced to strings; passing a config object with structured values; environment-variable naming confusion where a value field holds an object.

Related errors


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