Pumpkin-MC/Pumpkin · error

failed to add text-component resource

Error message

failed to add text-component resource

What it means

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.

Solutions

  1. Audit plugin handlers for ServerBroadcastEvent and ensure they consume/release the message and sender text-component resources so the table does not grow unbounded.
  2. 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.
  3. Verify the plugin instance is still alive/loaded when the event fires; skip dispatch for instances currently being unloaded.
  4. Update wasmtime and check resource-table limits; confirm no rep/insertion bug in PluginHostState::add_text_component.

Example fix

// before
let message = state
    .add_text_component(self.message.clone())
    .expect("failed to add text-component resource");
// after
let message = state
    .add_text_component(self.message.clone())
    .map_err(|e| anyhow::anyhow!("failed to add text-component resource: {e}"))?;
Defensive patterns

Strategy: fallback

Validate before calling

// host-side check before dispatch
if plugin_is_unloading(plugin_id) {
    tracing::warn!("skipping broadcast dispatch to unloading plugin {plugin_id}");
    return;
}

Type guard

fn is_table_healthy(state: &PluginHostState) -> bool {
    // table push only fails on exhausted/invalid tables; probe cheaply
    state.resource_table.iter().count() < RESOURCE_TABLE_SOFT_LIMIT
}

Try / catch

// replace expect with recoverable dispatch
match state.add_text_component(self.message.clone()) {
    Ok(msg) => { /* build event */ }
    Err(e) => {
        tracing::error!("broadcast dispatch failed: {e}");
        return Err(e.into());
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09). Data as JSON: /api/errors/03d43e195504a189. Report an issue: GitHub.

Appendix: source

Thrown at crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/server.rs:151

        })
    }

    fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self {
        match event {
            Event::ServerCommandEvent(data) => Self {
                command: data.command,
                cancelled: data.cancelled,
            },
            _ => panic!("unexpected event type"),
        }
    }
}

impl ToFromWasmEvent for ServerBroadcastEvent {
    fn to_wasm_event(&self, state: &mut PluginHostState) -> Event {
        let message = state
            .add_text_component(self.message.clone())
            .expect("failed to add text-component resource");
        let sender = state
            .add_text_component(self.sender.clone())
            .expect("failed to add text-component resource");

        Event::ServerBroadcastEvent(ServerBroadcastEventData {
            message,
            sender,
            cancelled: self.cancelled,
        })
    }

    fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self {
        match event {
            Event::ServerBroadcastEvent(data) => Self {
                message: consume_text_component(state, &data.message),
                sender: consume_text_component(state, &data.sender),
                cancelled: data.cancelled,
            },

View on GitHub (pinned to 8d4639e25a)