{"record":{"id":"03d43e195504a189","repo":"Pumpkin-MC/Pumpkin","slug":"failed-to-add-text-component-resource","errorCode":null,"errorMessage":"failed to add text-component resource","messagePattern":"failed to add text-component resource","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/server.rs","lineNumber":151,"sourceCode":"        })\n    }\n\n    fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self {\n        match event {\n            Event::ServerCommandEvent(data) => Self {\n                command: data.command,\n                cancelled: data.cancelled,\n            },\n            _ => panic!(\"unexpected event type\"),\n        }\n    }\n}\n\nimpl ToFromWasmEvent for ServerBroadcastEvent {\n    fn to_wasm_event(&self, state: &mut PluginHostState) -> Event {\n        let message = state\n            .add_text_component(self.message.clone())\n            .expect(\"failed to add text-component resource\");\n        let sender = state\n            .add_text_component(self.sender.clone())\n            .expect(\"failed to add text-component resource\");\n\n        Event::ServerBroadcastEvent(ServerBroadcastEventData {\n            message,\n            sender,\n            cancelled: self.cancelled,\n        })\n    }\n\n    fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self {\n        match event {\n            Event::ServerBroadcastEvent(data) => Self {\n                message: consume_text_component(state, &data.message),\n                sender: consume_text_component(state, &data.sender),\n                cancelled: data.cancelled,\n            },","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/server.rs#L133-L169","documentation":"Panic raised inside ServerBroadcastEvent::to_wasm_event when the host tries to insert the broadcast's message TextComponent into the wasmtime resource table via PluginHostState::add_text_component. The underlying `resource_table.push` returned an Err, meaning the WASM component instance's resource table rejected the new resource. Because the conversion runs on the host side inside an `.expect`, any table failure aborts the whole server thread rather than surfacing a recoverable error to the plugin.","triggerScenarios":"Calling to_wasm_event for a ServerBroadcastEvent while the wasmtime component resource table push fails — typically when the table is full (resource limit exhausted), the table belongs to a torn-down/plugin instance being unloaded, or a stored resource rep collided/was invalid. Triggered whenever a server broadcast event is dispatched to a WASM plugin and state.resource_table.push(TextComponentResource) errors.","commonSituations":"Dispatching chat/broadcast events to many WASM plugins that leak text-component resources (never consuming/releasing them), exhausting the per-instance resource table; firing the event during plugin shutdown while the instance table is already invalidated; host bugs or wasmtime resource-limit configuration issues.","solutions":["Audit plugin handlers for ServerBroadcastEvent and ensure they consume/release the message and sender text-component resources so the table does not grow unbounded.","Replace the `.expect(...)` with proper error propagation (return Result and use `?`) so a failing table push surfaces as a plugin dispatch error instead of a host panic.","Verify the plugin instance is still alive/loaded when the event fires; skip dispatch for instances currently being unloaded.","Update wasmtime and check resource-table limits; confirm no rep/insertion bug in PluginHostState::add_text_component."],"exampleFix":"// before\nlet message = state\n    .add_text_component(self.message.clone())\n    .expect(\"failed to add text-component resource\");\n// after\nlet message = state\n    .add_text_component(self.message.clone())\n    .map_err(|e| anyhow::anyhow!(\"failed to add text-component resource: {e}\"))?;","handlingStrategy":"fallback","validationCode":"// host-side check before dispatch\nif plugin_is_unloading(plugin_id) {\n    tracing::warn!(\"skipping broadcast dispatch to unloading plugin {plugin_id}\");\n    return;\n}","typeGuard":"fn is_table_healthy(state: &PluginHostState) -> bool {\n    // table push only fails on exhausted/invalid tables; probe cheaply\n    state.resource_table.iter().count() < RESOURCE_TABLE_SOFT_LIMIT\n}","tryCatchPattern":"// replace expect with recoverable dispatch\nmatch state.add_text_component(self.message.clone()) {\n    Ok(msg) => { /* build event */ }\n    Err(e) => {\n        tracing::error!(\"broadcast dispatch failed: {e}\");\n        return Err(e.into());\n    }\n}","preventionTips":["Always consume or drop text-component resources in plugin event handlers; never leak them.","Prefer `?`/Result propagation over `.expect` in host event conversion code.","Skip event dispatch for plugin instances mid-shutdown.","Monitor resource table size per plugin instance in production."],"tags":["wasm","resource-table","panic","events"],"backgroundTag":"internal-invariant-violation","analyzedSha":"8d4639e25a57c15e47448ec327c780d41bbf2356","analyzedAt":"2026-09-09T15:32:22.916Z","contentChangedAt":"2026-09-09T15:32:22.916Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}