Unity-Technologies/ml-agents · error · UnityAgentsException

GetCompressedObservation() returned null data for sensor nam

Error message

GetCompressedObservation() returned null data for sensor named {sensor.GetName()}. You must return a byte[]. If you don't want to use compressed observations, return CompressionSpec.Default() from GetCompressionSpec().

What it means

ML-Agents throws this when a sensor's GetCompressedObservation() returns null even though the sensor declared a non-default compression spec. The library promised compressed bytes over the gRPC channel but received nothing, so the observation proto cannot be built. It is a sensor implementation contract violation, not a runtime/environment failure.

Source

Thrown at com.unity.ml-agents/Runtime/Communicator/GrpcExtensions.cs:422

                {
                    floatDataProto.Data.Add(0.0f);
                }

                observationWriter.SetTarget(floatDataProto.Data, sensor.GetObservationSpec(), 0);
                sensor.Write(observationWriter);

                observationProto = new ObservationProto
                {
                    FloatData = floatDataProto,
                    CompressionType = (CompressionTypeProto)SensorCompressionType.None,
                };
            }
            else
            {
                var compressedObs = sensor.GetCompressedObservation();
                if (compressedObs == null)
                {
                    throw new UnityAgentsException(
                        $"GetCompressedObservation() returned null data for sensor named {sensor.GetName()}. " +
                        "You must return a byte[]. If you don't want to use compressed observations, " +
                        "return CompressionSpec.Default() from GetCompressionSpec()."
                    );
                }
                observationProto = new ObservationProto
                {
                    CompressedData = ByteString.CopyFrom(compressedObs),
                    CompressionType = (CompressionTypeProto)sensor.GetCompressionSpec().SensorCompressionType,
                };
                if (compressionSpec.CompressedChannelMapping != null)
                {
                    observationProto.CompressedChannelMapping.AddRange(compressionSpec.CompressedChannelMapping);
                }
            }

            // Add the dimension properties to the observationProto
            var dimensionProperties = obsSpec.DimensionProperties;

View on GitHub (pinned to 3ecb446f75)

Solutions

  1. Implement GetCompressedObservation() so it always returns a valid byte[] when a non-default CompressionSpec is declared
  2. Return CompressionSpec.Default() from GetCompressionSpec() if you don't need compression, so the uncompressed path is used
  3. Guard the compression path: if the compressed payload cannot be produced, fall back to uncompressed instead of returning null

Example fix

// before
public CompressionSpec GetCompressionSpec() => CompressionSpec.PNG();
public byte[] GetCompressedObservation() { return null; } // TODO
// after
public CompressionSpec GetCompressionSpec() => CompressionSpec.PNG();
public byte[] GetCompressedObservation() => EncodePng(m_Texture); // must never return null
Defensive patterns

Strategy: validation

Validate before calling

var spec = sensor.GetCompressionSpec();
if (spec != null && !spec.Equals(CompressionSpec.Default()))
{
    var bytes = sensor.GetCompressedObservation();
    if (bytes == null) throw new InvalidOperationException($"Sensor {sensor.GetName()} must return byte[] or use CompressionSpec.Default()");
}

Type guard

bool HasValidCompressedObservation(ISensor sensor) =>
    sensor.GetCompressionSpec() == null ||
    sensor.GetCompressionSpec().Equals(CompressionSpec.Default()) ||
    sensor.GetCompressedObservation() != null;

Try / catch

try { var obs = GrpcExtensions.GetObservationProto(sensor, ...); }
catch (UnityAgentsException e) when (e.Message.Contains("returned null data for sensor"))
{ Debug.LogError($"Fix sensor {sensor.GetName()}: implement GetCompressedObservation or return CompressionSpec.Default()"); }

Prevention

When it happens

Trigger: A custom SensorImplementation overrides GetCompressionSpec() to return e.g. PNG or MotionJpeg compression but its GetCompressedObservation() returns null (unimplemented, returns null on a stub, or compression failed silently). Only reached in GetObservationProto when compression is actually requested.

Common situations: Copy-pasting a custom sensor template without filling in GetCompressedObservation; a sensor whose underlying texture/camera capture fails and returns null; sensors that were built for uncompressed mode later switched to compressed training.

Related errors


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