Unity-Technologies/ml-agents · error · IndexOutOfRangeException

channel value {ch} must be in range [0, {m_TensorShape.Chann

Error message

channel value {ch} must be in range [0, {m_TensorShape.Channels() - 1}]

What it means

ObservationWriter's indexed setter validates that the channel coordinate falls within the observation tensor's channel count before writing. Writing outside that range would overwrite neighboring observations in the flat backing array, so an IndexOutOfRangeException is thrown.

Source

Thrown at com.unity.ml-agents/Runtime/Sensors/ObservationWriter.cs:152

        public float this[int ch, int h, int w]
        {
            set
            {
                if (m_Data != null)
                {
                    if (h < 0 || h >= m_TensorShape.Height())
                    {
                        throw new IndexOutOfRangeException($"height value {h} must be in range [0, {m_TensorShape.Height() - 1}]");
                    }

                    if (w < 0 || w >= m_TensorShape.Width())
                    {
                        throw new IndexOutOfRangeException($"width value {w} must be in range [0, {m_TensorShape.Width() - 1}]");
                    }

                    if (ch < 0 || ch >= m_TensorShape.Channels())
                    {
                        throw new IndexOutOfRangeException($"channel value {ch} must be in range [0, {m_TensorShape.Channels() - 1}]");
                    }

                    var index = m_TensorShape.Index(m_Batch, ch + m_Offset, h, w);
                    m_Data[index] = value;
                }
                else
                {
                    if (ch + m_Offset < 0 || ch + m_Offset >= m_TensorShape.Channels() ||
                        h < 0 || h >= m_TensorShape.Height() ||
                        w < 0 || w >= m_TensorShape.Width())
                        return;

                    m_Proxy.data.CompleteAllPendingOperations();
                    ((Tensor<float>)m_Proxy.data)[m_Batch, ch + m_Offset, h, w] = value;
                }
            }
        }

View on GitHub (pinned to 3ecb446f75)

Solutions

  1. Limit the channel loop to spec.Channels() (and respect the writer's offset for stacked writers)
  2. Make the ObservationSpec channel count match the data written (e.g. 3 for RGB, 1 for grayscale)
  3. Use ObservationWriter helper APIs (WriteTex2D/Add) that handle channels and offsets correctly
  4. Check for off-by-one loops (ch <= channels instead of ch < channels)

Example fix

// before
for (int ch = 0; ch < 4; ch++) writer[0, 0, ch] = rgba[ch]; // spec has 3 channels
// after
for (int ch = 0; ch < obsSpec.Channels(); ch++) writer[0, 0, ch] = rgba[ch];
Defensive patterns

Strategy: validation

Validate before calling

// Unity C#: in custom sensor Write
if (ch < 0 || ch >= obsSpec.Channels())
{
    Debug.LogError($"ch={ch} outside [0,{obsSpec.Channels() - 1}]");
    return;
}
writer[h, w, ch] = value;

Try / catch

try
{
    writer[h, w, ch] = value;
}
catch (IndexOutOfRangeException e) when (e.Message.Contains("channel value"))
{
    Debug.LogError("Channel out of bounds writing observation: " + e.Message);
}

Prevention

When it happens

Trigger: A custom sensor's Write() implementation calling Write(h, w, ch, value) with ch < 0 or ch >= channel count (e.g. writing RGB with 3 channels while the spec declares 1 channel, or writing a 4-channel RGBA loop); m_Offset plus ch exceeding the declared channels.

Common situations: Custom sensors writing RGBA into a grayscale observation; stacked sensors where the offset wasn't accounted for; changing channel count in the spec but not in the write loop.

Related errors


AI-assisted analysis of Unity-Technologies/ml-agents@3ecb446f75 (2026-09-02). Data as JSON: /api/errors/221a5ea5061c6124. Report an issue: GitHub.