OpenRA/OpenRA · error · LuaException

Layer {layer} does not exist on this map. Valid layers on th

Error message

Layer {layer} does not exist on this map. Valid layers on this map are: {string.Join(", ", validLayers.Select(x => $"{x.Index} ({x.Name})"))}

What it means

When constructing a CPos with a non-zero layer, the engine checks the world's custom movement layers array; if the index is out of range or that slot is null (no layer registered at that index) it lists the valid layers by index and name and aborts.

Source

Thrown at OpenRA.Mods.Common/Scripting/Global/CoordinateGlobals.cs:44

		[Desc("Create a new CPos with the specified coordinates on the specified layer. " +
			"The ground is layer 0, other layers have a unique ID. Examples include tunnels, underground, and elevated bridges.")]
		public CPos NewWithLayer(int x, int y, byte layer)
		{
			if (layer != 0)
			{
				var worldCmls = Context.World.GetCustomMovementLayers();
				if (layer >= worldCmls.Length || worldCmls[layer] == null)
				{
					var layerNames = typeof(CustomMovementLayerType)
						.GetFields()
						.Select(f => (Index: (byte)f.GetRawConstantValue(), f.Name))
						.ToArray();
					var validLayers = new[] { (Index: (byte)0, Name: "Ground") }
						.Concat(worldCmls
							.Where(cml => cml != null)
							.Select(cml => layerNames.Single(ln => ln.Index == cml.Index)));
					throw new LuaException($"Layer {layer} does not exist on this map. " +
						$"Valid layers on this map are: {string.Join(", ", validLayers.Select(x => $"{x.Index} ({x.Name})"))}");
				}
			}

			return new CPos(x, y, layer);
		}

		[Desc("The cell coordinate origin.")]
		public CPos Zero => CPos.Zero;
	}

	[ScriptGlobal("CVec")]
	public class CVecGlobal : ScriptGlobal
	{
		public CVecGlobal(ScriptContext context)
			: base(context) { }

		[Desc("Create a new CVec with the specified coordinates.")]

View on GitHub (pinned to a520984d91)

Solutions

  1. Read the valid layers from the error message and use one of those indices.
  2. Use layer 0 (Ground) when no custom layer is needed.
  3. Ensure the map includes the movement-layer trait (e.g. TunnelLayer, BridgeLayer) if a custom layer is required.

Example fix

-- before
local p = CPos.New(5, 5, 2)  -- layer 2 not on this map
-- after
local p = CPos.New(5, 5, 0)  -- Ground layer
-- or use a valid layer index from the error's list
Defensive patterns

Strategy: validation

Validate before calling

-- Use layer 0 (Ground) unless a known custom layer index is required.
local function SafeCPos(x, y, layer)
  layer = layer or 0
  if layer ~= 0 and not ValidLayers[layer] then
    layer = 0
  end
  return CPos.New(x, y, layer)
end

Type guard

local function IsValidLayer(idx)
  return type(idx) == "number" and (idx == 0 or ValidLayers[idx] == true)
end

Try / catch

local ok, p = pcall(CPos.New, x, y, layer)
if not ok then return CPos.New(x, y, 0) end

Prevention

When it happens

Trigger: Calling CPos.New(x, y, layer) with a layer index for a custom movement layer (e.g. tunnels, bridges) that the map does not provide; passing an index beyond the registered layers.

Common situations: Map without the required custom-layer traits; using a layer index valid in one map but not another; stale index after the map's layers changed.

Related errors


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