stride3d/stride · error · ArgumentNullException

heightStickArray

Error message

heightStickArray

What it means

ShortHeightStickArraySource.CopyTo throws ArgumentNullException named 'heightStickArray' when the destination UnmanagedArray is null. This type fills a pre-allocated unmanaged heightfield buffer with the terrain's initial short height value; a null destination cannot be written to.

Solutions

  1. Allocate the UnmanagedArray before calling CopyTo, sized to HeightStickSize.X * HeightStickSize.Y in the correct element type
  2. Ensure the source array type matches: a ShortHeightStickArraySource requires UnmanagedArray<short> (the generic else branch throws for other types)
  3. Check that the buffer was not disposed before CopyTo runs; keep it alive until the heightfield collider consumes it

Example fix

// before
UnmanagedArray<short> sticks = null;
heightSource.CopyTo(sticks, 0); // ArgumentNullException: heightStickArray

// after
var sticks = new UnmanagedArray<short>(heightSource.HeightStickSize.X * heightSource.HeightStickSize.Y);
heightSource.CopyTo(sticks, 0);
Defensive patterns

Strategy: validation

Validate before calling

static void EnsureHeightBufferAllocated(UnmanagedArray<short> buffer, Int2 size)
{
    if (buffer == null)
        throw new InvalidOperationException("Allocate UnmanagedArray<short> of size " +
            (size.X * size.Y) + " before calling CopyTo");
}

Type guard

bool IsReadyForCopyTo<T>(ShortHeightStickArraySource src, UnmanagedArray<T> buf) where T : struct =>
    src != null && src.IsValid() && buf != null && buf is UnmanagedArray<short>;

Try / catch

try { heightSource.CopyTo(sticks, 0); }
catch (ArgumentNullException ex) when (ex.ParamName == "heightStickArray")
{
    sticks = new UnmanagedArray<short>(heightSource.HeightStickSize.X * heightSource.HeightStickSize.Y);
    heightSource.CopyTo(sticks, 0);
}

Prevention

When it happens

Trigger: Calling CopyTo<T>(UnmanagedArray<T>, int) on a ShortHeightStickArraySource with a null heightStickArray argument — typically when the heightfield buffer failed to allocate or was never created before the copy step (e.g. setting up a HeightfieldColliderShape).

Common situations: Terrain/collider setup code where the UnmanagedArray allocation was skipped, wrapped in a using block that already disposed it, or passed through from an uninitialized field; copy/paste setups using the float variant while holding a short-source variable.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/4395fe1f187bf6c2. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Physics/ShortHeightStickArraySource.cs:47

        /// </summary>
        [DataMember(30)]
        [NotNull]
        [Display("HeightScale", Expand = ExpandRule.Always)]
        public IHeightScaleCalculator HeightScaleCalculator { get; set; } = new HeightScaleCalculator();

        /// <summary>
        /// The value to fill the height stick array.
        /// </summary>
        [DataMember(40)]
        [DataMemberRange(-32767, 32767, 1, 10, 0)]
        public short InitialShort { get; set; } = 0;

        public bool IsValid() => HeightmapUtils.CheckHeightParameters(HeightStickSize, HeightType, HeightRange, HeightScale, false) &&
            MathUtil.IsInRange(InitialShort, -short.MaxValue, short.MaxValue);

        public void CopyTo<T>(UnmanagedArray<T> heightStickArray, int index) where T : struct
        {
            if (heightStickArray == null) throw new ArgumentNullException(nameof(heightStickArray));
            if (heightStickArray is UnmanagedArray<short> unmanagedArray)
            {
                unmanagedArray.Fill(InitialShort, index, HeightStickSize.X * HeightStickSize.Y);
            }
            else
            {
                throw new NotSupportedException($"{ typeof(UnmanagedArray<T>) } type is not supported.");
            }
        }

        public bool Match(object obj)
        {
            var other = obj as ShortHeightStickArraySource;

            if (other == null)
            {
                return false;
            }

View on GitHub (pinned to 96fad776d2)