OpenRA/OpenRA · error · YamlException

Weapon '{weaponToLower}' has an invalid number of BurstDelay

Error message

Weapon '{weaponToLower}' has an invalid number of BurstDelays, must be single entry or Burst - 1.

What it means

Thrown by ArmamentInfo.RulesetLoaded when a multi-shot weapon defines a BurstDelays list whose length is neither 1 (single shared delay) nor Burst-1 (one delay between each pair of shots). The engine needs exactly one delay per inter-shot gap, so any other count is invalid and a YamlException is raised.

Source

Thrown at OpenRA.Mods.Common/Traits/Armament.cs:104

		[Desc("Ammo the weapon consumes per shot.")]
		public readonly int AmmoUsage = 1;

		public override object Create(ActorInitializer init) { return new Armament(init.Self, this); }

		public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
		{
			var weaponToLower = Weapon.ToLowerInvariant();
			if (!rules.Weapons.TryGetValue(weaponToLower, out var weaponInfo))
				throw new YamlException($"Weapons Ruleset does not contain an entry '{weaponToLower}'");

			WeaponInfo = weaponInfo;
			ModifiedRange = new WDist(Util.ApplyPercentageModifiers(
				WeaponInfo.Range.Length,
				ai.TraitInfos<IRangeModifierInfo>().Select(m => m.GetRangeModifierDefault())));

			if (WeaponInfo.Burst > 1 && WeaponInfo.BurstDelays.Length > 1 && (WeaponInfo.BurstDelays.Length != WeaponInfo.Burst - 1))
				throw new YamlException($"Weapon '{weaponToLower}' has an invalid number of BurstDelays, must be single entry or Burst - 1.");

			if (WeaponInfo.ReloadDelay <= 0)
				throw new YamlException($"Weapon '{weaponToLower}' ReloadDelay value must not be equal to or lower than 0");

			base.RulesetLoaded(rules, ai);
		}
	}

	public class Armament : PausableConditionalTrait<ArmamentInfo>, ITick
	{
		public readonly WeaponInfo Weapon;
		public readonly Barrel[] Barrels;
		Turreted turret;
		Hovers hovers;

		BodyOrientation coords;
		INotifyBurstComplete[] notifyBurstComplete;
		List<(Actor NotifyActor, INotifyAttack Notify)> notifyAttacks;

View on GitHub (pinned to a520984d91)

Solutions

  1. Set BurstDelays to a single value (applies to every gap) OR exactly Burst-1 entries.
  2. Recount BurstDelays entries to equal Burst-1 when providing per-gap delays.
  3. When changing Burst, immediately re-derive the BurstDelays list length.

Example fix

# before (invalid: Burst 4, 2 delays)
Weapon: baz
Burst: 4
BurstDelays: 20, 40

# after (per-gap: Burst-1 = 3 delays)
Weapon: baz
Burst: 4
BurstDelays: 20, 40, 20
Defensive patterns

Strategy: validation

Validate before calling

// Validate BurstDelays length against Burst
if (burst > 1 && burstDelays.Length > 1 && burstDelays.Length != burst - 1)
    throw new YamlException($"BurstDelays length {burstDelays.Length} must be 1 or Burst-1={burst - 1}.");

Type guard

bool ValidBurstDelays(int burst, int delays) =>
    burst <= 1 || delays <= 1 || delays == burst - 1;

Prevention

When it happens

Trigger: A weapon with Burst>1 and a BurstDelays list whose length is >1 and != Burst-1 (e.g. Burst=4 but BurstDelays has 2 or 4 entries). The check only applies when Burst>1 and BurstDelays.Length>1.

Common situations: Editing Burst without updating BurstDelays; copy-pasting delays from a weapon with a different Burst count; misunderstanding that BurstDelays of length Burst-1 covers gaps between consecutive shots.

Related errors


AI-assisted analysis of OpenRA/OpenRA@a520984d91 (2026-08-13). Data as JSON: /api/errors/c793a6655c5291e8. Report an issue: GitHub.