{"record":{"id":"a8ab544c3722d8aa","repo":"Unity-Technologies/ml-agents","slug":"statssidechannel-should-never-receive-messages","errorCode":null,"errorMessage":"StatsSideChannel should never receive messages.","messagePattern":"StatsSideChannel should never receive messages\\.","errorType":"exception","errorClass":"UnityAgentsException","httpStatus":null,"severity":"error","filePath":"com.unity.ml-agents/Runtime/SideChannels/StatsSideChannel.cs","lineNumber":40,"sourceCode":"        /// </summary>\n        /// <param name=\"key\">The stat name.</param>\n        /// <param name=\"value\">The stat value.</param>\n        /// <param name=\"aggregationMethod\">How multiple values should be treated.</param>\n        public void AddStat(string key, float value, StatAggregationMethod aggregationMethod)\n        {\n            using (var msg = new OutgoingMessage())\n            {\n                msg.WriteString(key);\n                msg.WriteFloat32(value);\n                msg.WriteInt32((int)aggregationMethod);\n                QueueMessageToSend(msg);\n            }\n        }\n\n        /// <inheritdoc/>\n        protected override void OnMessageReceived(IncomingMessage msg)\n        {\n            throw new UnityAgentsException(\"StatsSideChannel should never receive messages.\");\n        }\n    }\n}\n","sourceCodeStart":22,"sourceCodeEnd":44,"githubUrl":"https://github.com/Unity-Technologies/ml-agents/blob/3ecb446f75d1e7400eb404c562dc005d3164cffc/com.unity.ml-agents/Runtime/SideChannels/StatsSideChannel.cs#L22-L44","documentation":"StatsSideChannel is a write-only channel: Unity pushes training statistics to Python and never consumes incoming messages. Its OnMessageReceived override unconditionally throws UnityAgentsException to assert this contract. Receiving any message on this channel id means the Python side is sending data on a channel reserved for Unity output.","triggerScenarios":"Python code writing to the stats side channel GUID (via a custom SideChannel in mlagents-envs with the StatsSideChannel channel id) so Unity receives an incoming message on StatsSideChannel and throws.","commonSituations":"A custom Python tool echoing messages back on the stats channel id; wiring the wrong channel object on the Python side so stats GUID is used bidirectionally; a bug in custom interop code reusing built-in channel GUIDs for requests.","solutions":["Remove Python code that sends messages on the StatsSideChannel channel id; stats flow Unity -> Python only.","If you need Unity->Python custom messages, create a new SideChannel pair with its own unique GUID on both sides.","Audit the Python side channel registration to confirm the stats GUID isn't attached to a send path."],"exampleFix":"# before\nstats_channel.send_raw_message(...)  # Python sending on stats GUID: forbidden\n# after\nclass MyRequestChannel(SideChannel):\n    channel_id = uuid.UUID(\"<new-unique-guid>\")\nmy_channel = MyRequestChannel()\nenv.reset()\n# communicate via my_channel instead","handlingStrategy":"validation","validationCode":"# Python: never attach a send path to the stats channel GUID\nassert not any(c.channel_id == STATS_CHANNEL_GUID for c in sending_channels)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat StatsSideChannel as write-only (Unity -> Python)","Create a dedicated GUID pair for any Python -> Unity messages","Never reuse built-in channel GUIDs for custom traffic"],"tags":["unity","python","ml-agents","side-channel","contract-violation","stats"],"backgroundTag":"unexpected-inbound-message","analyzedSha":"3ecb446f75d1e7400eb404c562dc005d3164cffc","analyzedAt":"2026-09-02T16:33:12.832Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T21:17:11.164Z"}