Skip to content

Errors and Edge Cases

m4bwav edited this page Oct 4, 2026 · 1 revision

UniverseGenerator refuses bad input before it generates anything, with a message that says what to change, and once generation starts it never throws. Settings that are valid but change nothing or read badly are warnings, returned as values. This page lists every refusal the verification run could produce, with its exact message on 1.0.0.

What is refused

using UniverseGeneration;

void Try(string what, Action action)
{
    try
    {
        action();
        Console.WriteLine($"{what}: no error");
    }
    catch (Exception e)
    {
        Console.WriteLine($"{what}: {e.GetType().Name}: {e.Message}");
    }
}

Try("Systems = 0", () => Galaxy.Generate("my-seed", Preset.Default with { Systems = 0 }));
Try("Systems = 2001", () => Galaxy.Generate("my-seed", Preset.Default with { Systems = 2001 }));
Try("Weirdness = 101", () => Galaxy.Generate("my-seed", Preset.Default with { Weirdness = 101 }));
Try("Arms = 5", () => Galaxy.Generate("my-seed", Preset.Default with { Arms = 5 }));
Try("DangerShift = 6", () => Galaxy.Generate("my-seed", Preset.Default with { DangerShift = 6 }));
Try("a 201-character seed", () => Galaxy.Generate(new string('a', 201)));
Try("an empty seed", () => Galaxy.Generate(""));
Try("a null seed", () => Galaxy.Generate((string)null!));
Try("System(60) of 60", () => Galaxy.Generate("my-seed").System(60));
Try("an unknown level", () => Universe.At("v1-my-seed/galaxy/nebula/0"));
Try("system 60 of 60", () => Universe.At("v1-my-seed/galaxy/system/60"));
Try("generator version 2", () => Universe.At("v2-my-seed/galaxy"));
Try("a link and options", () => Universe.At("v1-my-seed/galaxy?systems=120", Preset.Default));
Try("an unknown option code", () => GeneratorOptions.FromCode("systems=120&colour=red"));
Try("unregistered tables", () => Galaxy.Generate("my-seed", Preset.Default with { Tables = "nobody-registered-this" }));
Try("StarName count 0", () => StarName.Generate("my-seed", 0));
Try("StarName count 1001", () => StarName.Generate("my-seed", 1001));
Try("tables from broken JSON", () => GeneratorTables.FromJson("{"));

prints on .NET 10:

Systems = 0: ArgumentException: Systems must be 1 to 2000; you asked for 0. (Parameter 'Systems')
Systems = 2001: ArgumentException: Systems must be 1 to 2000; you asked for 2001. (Parameter 'Systems')
Weirdness = 101: ArgumentException: Weirdness must be 0 to 100; you asked for 101. (Parameter 'Weirdness')
Arms = 5: ArgumentException: Arms must be 2 to 4; you asked for 5. (Parameter 'Arms')
DangerShift = 6: ArgumentException: DangerShift must be -5 to 5; you asked for 6. (Parameter 'DangerShift')
a 201-character seed: ArgumentException: A seed must be at most 200 characters; yours has 201. (Parameter 'seed')
an empty seed: no error
a null seed: ArgumentNullException: A seed is any text, such as "my-seed" or "42"; it cannot be null. (Parameter 'seed')
System(60) of 60: ArgumentOutOfRangeException: This galaxy has 60 systems, numbered 0 to 59; you asked for 60. (Parameter 'index')
Actual value was 60.
an unknown level: ArgumentException: Below v1-my-seed/galaxy comes a system; the address has "nebula". (Parameter 'address')
system 60 of 60: ArgumentException: v1-my-seed/galaxy has 60 systems (numbered 0 to 59); the address asks for system 60. Were the same options passed as when it was generated? (Parameter 'address')
generator version 2: ArgumentException: This package generates version 1; the address asks for version 2. (Parameter 'address')
a link and options: ArgumentException: This link carries its options after the ?; pass a link or options, not both. (Parameter 'address')
an unknown option code: ArgumentException: "colour" is not a setting; the settings are systems, shape, starmix, weirdness, planets, cluster, epoch, arms, lanes, danger, names, require, tables.
unregistered tables: ArgumentException: No tables are registered as "nobody-registered-this"; call GeneratorTables.Register first. (Parameter 'Tables')
StarName count 0: ArgumentException: Count must be 1 to 1000; you asked for 0. (Parameter 'count')
StarName count 1001: ArgumentException: Count must be 1 to 1000; you asked for 1001. (Parameter 'count')
tables from broken JSON: FormatException: Tables JSON, at character 1: expected ".

On .NET Framework 4.8 the messages are the same, but the runtime prints the parameter on a line of its own instead of in brackets, so a message that is shown to users or matched in a test differs between the two:

Systems = 0: ArgumentException: Systems must be 1 to 2000; you asked for 0.
Parameter name: Systems

Match on the exception type and ParamName, not on the whole Message.

What to take from it

  • Catch ArgumentException around anything that takes user input: options, seeds, addresses, links and option codes. ArgumentNullException and ArgumentOutOfRangeException derive from it. GeneratorTables.FromJson throws FormatException for JSON it cannot read.
  • An empty seed is a seed. Only null and seeds over 200 characters (GeneratorOptions.MaxSeedLength) are refused. If an empty text box should not be a world, check for it yourself.
  • An address that worked can fail with other options. "system 60" of a 60-system galaxy is refused, and the message asks whether the same options were passed: an address does not carry options, a link does (Recipes).
  • A link carries its own options, so Universe.At(link, options) is refused rather than guessing which to use.
  • Tables must be registered first in every program that reads a galaxy made with them, or Generate and Universe.At refuse the id.

Warnings, never errors

options.Check() lists settings that are valid but change nothing or read badly, and galaxy.Warnings says what generation had to change. Both are GeneratorWarning values with a WarningCode and a message:

using UniverseGeneration;

var ring = Preset.Default with { Shape = GalaxyShape.Ring, Arms = 3 };
foreach (var w in ring.Check())
    Console.WriteLine($"{w.Code}: {w.Message}");

var small = Preset.Default with { Shape = GalaxyShape.Spiral, Systems = 12 };
foreach (var w in small.Check())
    Console.WriteLine($"{w.Code}: {w.Message}");

var barren = Preset.Default with { MaxPlanetsPerSystem = 0, Require = Guarantee.GardenWorld };
foreach (var w in Galaxy.Generate("my-seed", barren).Warnings)
    Console.WriteLine($"{w.Code}: {w.Message}");

prints:

ArmsIgnored: Arms = 3 changes nothing: a ring galaxy has no arms.
ShapeTooSmall: A spiral galaxy of 12 systems: arms read on a map only from 80 systems.
GuaranteeUnmet: Require GardenWorld is not met: no system has a temperate rocky planet to hold it.

The warning codes in 1.0.0 are SystemsTrimmed, ArmsIgnored, ShapeTooSmall, NameNumbered and GuaranteeUnmet (API reference).

Edge cases

  • Systems from 1 to 2,000 (GeneratorOptions.MaxSystems). Systems are built when first read, so a large galaxy costs little until you open it.
  • MaxPlanetsPerSystem = 0 is accepted; a guarantee that needs a planet then warns, as above.
  • Arms is null by default, which lets the generator choose; 2 to 4 are accepted, and they are ignored (with a warning) on shapes without arms.
  • Colliding, Starburst and Clustered shapes are made only when you ask for them by name; GalaxyShape.Auto never picks them.
  • StarName.Generate(seed, count) takes 1 to 1,000 (StarName.MaxCount).
  • Option codes are lowercase name=value pairs joined by &, and an unknown name is refused with the list of known ones.

Clone this wiki locally