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
- Implement GetCompressedObservation() so it always returns a valid byte[] when a non-default CompressionSpec is declared
- Return CompressionSpec.Default() from GetCompressionSpec() if you don't need compression, so the uncompressed path is used
- 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
- Never return null from GetCompressedObservation in custom sensors
- Only declare a non-default CompressionSpec when compression is actually implemented
- Unit-test custom sensors: assert GetCompressedObservation() != null when compression is declared
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
- No parent indices set
- Sensor {sensor.GetName()} have an invalid rank {rank}
- When using compression type {m_CompressionType} the data val
- Index out of bounds, expected a number between 0 and {Length
- Enumerator not started.
AI-assisted analysis of Unity-Technologies/ml-agents@3ecb446f75 (2026-09-02).
Data as JSON: /api/errors/4329bfda41e7e94e.
Report an issue: GitHub.