{"record":{"id":"572327e08eba208a","repo":"FyroxEngine/Fyrox","slug":"a-brush-painting-message-was-not-sent-error","errorCode":null,"errorMessage":"A brush painting message was not sent. {error:?}","messagePattern":"A brush painting message was not sent\\. (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fyrox-impl/src/scene/terrain/brushstroke/mod.rs","lineNumber":228,"sourceCode":"    }\n    /// Draw a pixel using the brush that was set in the most recent call to [BrushSender::start_stroke].\n    #[inline]\n    pub fn draw_pixel(&self, position: Vector2<i32>, alpha: f32, value: f32) {\n        if alpha == 0.0 {\n            return;\n        }\n        self.0\n            .send(BrushThreadMessage::Pixel(BrushPixelMessage {\n                position,\n                alpha,\n                value,\n            }))\n            .unwrap_or_else(on_send_failure);\n    }\n}\n\nfn on_send_failure(error: SendError<BrushThreadMessage>) {\n    Log::err(format!(\"A brush painting message was not sent. {error:?}\"));\n}\n\n/// Type for a callback that delivers the original data of textures that have been modified\n/// by the brush so that changes might be undone.\npub type UndoChunkHandler = dyn FnMut(UndoData) + Send;\n\n/// A record of original data data for chunks that have been modified by a brushstroke.\npub struct UndoData {\n    /// The handle of the terrain being edited\n    pub node: Handle<Node>,\n    /// The data of the chunks as they were before the brushstroke.\n    pub chunks: Vec<ChunkData>,\n    /// The kind of data within the terrain that is being edited.\n    pub target: BrushTarget,\n}\n\n#[derive(Default)]\n/// Data for an in-progress terrain painting operation","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/FyroxEngine/Fyrox/blob/76c91aad8eca488ce527b1af707be8b3b24ad72d/fyrox-impl/src/scene/terrain/brushstroke/mod.rs#L210-L246","documentation":"Terrain brush painting communicates with a background worker thread via a channel; on_send_failure is the unwrap_or_else handler invoked when sending a BrushThreadMessage fails (receiver dropped). It logs that the painting command was not delivered.","triggerScenarios":"BrushStroke operations (apply/commit/reset) calling message send on the brush thread channel after the worker thread has exited, producing SendError<BrushThreadMessage> handled by on_send_failure.","commonSituations":"Terrain destroyed or brush worker shut down while painting is still in progress; editor shutdown racing an in-flight stroke; a panic previously killed the worker thread.","solutions":["Ensure the terrain and its brush thread outlive all brush painting calls (don't drop/destroy terrain mid-stroke).","Check earlier logs for a brush worker panic that terminated the receiving thread.","Guard brush UI/input handling so strokes end (finish_stroke) before terrain teardown."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Before painting, verify the terrain handle still points to a live node with an active brush:\nif let Some(terrain) = scene.graph.try_get(terrain_handle).ok() {\n    terrain.paint(brush, stroke);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never destroy terrain nodes while a brush stroke is in progress.","Finish strokes before scene shutdown/teardown.","Watch logs for earlier brush-thread panics that kill the worker."],"tags":["terrain","threading","channel","editor"],"backgroundTag":"broken-pipe","analyzedSha":"76c91aad8eca488ce527b1af707be8b3b24ad72d","analyzedAt":"2026-09-10T16:04:01.633Z","contentChangedAt":"2026-09-10T16:04:01.633Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}