dotnet/runtime · critical

max-heap-size must be at least %dMb.\n

Error message

max-heap-size must be at least %dMb.\n

What it means

Emitted during Mono Boehm GC initialization in mono_gc_base_init (boehm-gc.c:171). The MONO_GC_PARAMS environment variable is comma-split and a `max-heap-size=` token is parsed by mono_gc_parse_environment_string_extract_number (which accepts an optional k/m/g suffix, converted to bytes via shifts). If the parsed value is below MIN_BOEHM_MAX_HEAP_SIZE (16 << 20 = 16 MiB) the runtime prints 'max-heap-size must be at least 16Mb.' and immediately calls exit(1) — the process dies at GC init, before managed code runs.

Source

Thrown at src/mono/mono/metadata/boehm-gc.c:171

	GC_set_warn_proc (mono_gc_warning);
	GC_set_finalize_on_demand (1);
	GC_set_finalizer_notifier(mono_gc_finalize_notify);

	GC_init_gcj_malloc (5, NULL);
	GC_allow_register_threads ();

	if ((env = g_getenv ("MONO_GC_PARAMS"))) {
		char **ptr, **opts = g_strsplit (env, ",", -1);
		for (ptr = opts; *ptr; ++ptr) {
			char *opt = *ptr;
			if (g_str_has_prefix (opt, "max-heap-size=")) {
				size_t max_heap;

				opt = strchr (opt, '=') + 1;
				if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
					if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
						fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
						exit (1);
					}
					GC_set_max_heap_size (max_heap);
				} else {
					fprintf (stderr, "max-heap-size must be an integer.\n");
					exit (1);
				}
				continue;
			} else if (g_str_has_prefix (opt, "toggleref-test")) {
				register_test_toggleref_callback ();
				continue;
			} else {
				/* Could be a parameter for sgen */
				/*
				fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
				fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
				exit (1);
				*/

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Raise the value to at least 16m, e.g. MONO_GC_PARAMS=max-heap-size=64m.
  2. Remember the floor is 16 MiB: a suffixless number is bytes, 'k'=KiB, 'm'=MiB, 'g'=GiB; values like 15m and anything smaller are rejected.
  3. If you genuinely need a tiny heap, Boehm is the wrong collector — switch the runtime to sgen (which accepts smaller nurseries) or revisit the constraint.
  4. Validate MONO_GC_PARAMS in your launch wrapper before starting the runtime so a bad value fails fast with a clear message instead of exit(1).
  5. On Mono, confirm the active GC with `mono --version` / the runtime build; boehm-gc.c is only compiled when HAVE_BOEHM_GC is defined.

Example fix

# before
$ MONO_GC_PARAMS=max-heap-size=8m mono app.exe
max-heap-size must be at least 16Mb.
# (process exits)

# after
$ MONO_GC_PARAMS=max-heap-size=64m mono app.exe
Defensive patterns

Strategy: validation

Validate before calling

# Parse MONO_GC_PARAMS exactly like the runtime and reject values below the 16 MiB Boehm floor.
python3 - <<'PY'
import os, re
params = os.environ.get('MONO_GC_PARAMS', '')
for tok in params.split(','):
    m = re.match(r'^max-heap-size=(.*)$', tok.strip())
    if not m: continue
    val = m.group(1)
    if not re.match(r'^\d+[kKmMgG]?$', val):
        continue
    suffix, shift = val[-1].lower(), 0
    num = int(re.match(r'^\d+', val).group(0))
    if suffix in 'kmg': shift = {'k':1,'m':2,'g':3}[suffix] * 10
    bytes_ = num << shift
    if bytes_ < (16 << 20):
        raise SystemExit(f'ERROR: max-heap-size={val} ({bytes_} bytes) < Boehm floor 16MiB; use >=16m')
print('MONO_GC_PARAMS OK')
PY
exec mono "$@"

Prevention

When it happens

Trigger: Launching a Mono runtime built with the Boehm GC (HAVE_BOEHM_GC) with MONO_GC_PARAMS containing `max-heap-size=<n>` where <n> resolves to fewer than 16 MiB after suffix expansion, e.g. max-heap-size=8, max-heap-size=15m, max-heap-size=1000k. mono_gc_base_init() runs once at startup; the MIN_BOEHM_MAX_HEAP_SIZE floor is hardcoded at boehm-gc.c:52-53.

Common situations: Operator sets a heap cap to constrain memory and picks a value below the Boehm floor (16m); confusing suffixes (assuming m=KB or that the number is already in MB when suffixless means bytes); copying an sgen-style value into a Boehm build; container memory-constraint scripts that autogenerate small max-heap-size values; typo like max-heap-size=1m expecting 1 GB.

Related errors


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