Pumpkin-MC/Pumpkin · error

failed to add world resource

Error message

failed to add world resource

What it means

SpawnChangeEvent::to_wasm_event calls PluginHostState::add_world, which pushes a WorldResource into the wasmtime component resource table, and unwraps the result with expect(). The only failure path is the wasmtime ResourceTable push failing (e.g. the table's resource limit is exhausted or its internal store is corrupt), so this panic means the plugin host could not register a handle for the event's world.

Solutions

  1. Audit plugin event handling so world/chunk resources pushed into the table are consumed and dropped via the WIT resource-drop path, preventing table exhaustion
  2. Restart the affected plugin instance (recreate PluginHostState) to get a fresh resource table
  3. Update wasmtime / pumpkin to a version with resource-limit fixes, or raise the component resource limits in the wasmtime store configuration
  4. Check server logs for earlier table/store errors indicating a corrupted PluginHostState

Example fix

// before
let world = state
    .add_world(self.world.clone())
    .expect("failed to add world resource");

// after
let world = state.add_world(self.world.clone()).map_err(|e| {
    log::error!("failed to add world resource: {e}");
    e
})?;
Defensive patterns

Strategy: try-catch

Validate before calling

// before dispatching events to plugins, confirm the host state is usable
fn can_push_resources(state: &PluginHostState) -> bool {
    // wasmtime ResourceTable grows dynamically; failure indicates store corruption
    !state.is_poisoned()
}

Type guard

fn is_valid_wasm_host_state(state: &PluginHostState) -> bool {
    !state.is_poisoned()
}

Try / catch

// host side: never expect(); convert to Result and quarantine the plugin
match state.add_world(world.clone()) {
    Ok(res) => /* continue building event */,
    Err(e) => {
        log::error!("wasm host resource push failed: {e}");
        disable_plugin(instance_id);
    }
}

Prevention

When it happens

Trigger: A spawn-position-change event is being converted to a WIT event for a plugin while PluginHostState.resource_table cannot accept a new WorldResource entry — wasmtime's resource table is at capacity (leaked world/chunk resources never consumed) or the table is in a broken state.

Common situations: Long-running servers with many plugins where world resources are pushed per event but plugin instances leak them until the table's rep limit is hit; a wasmtime store/table that failed mid-lifetime; hosting an unusually large number of simultaneous plugin instances sharing one state.

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/83b4ac1b2e7c9624. Report an issue: GitHub.

Appendix: source

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

            },
            pumpkin::plugin::event::{
                ChunkLoadEventData, ChunkSaveEventData, ChunkSendEventData, Event,
                SpawnChangeEventData, ThunderChangeEventData, WeatherChangeEventData,
                WorldLoadEventData, WorldUnloadEventData,
            },
        },
    },
    world::{
        chunk_load::ChunkLoad, chunk_save::ChunkSave, chunk_send::ChunkSend,
        spawn_change::SpawnChangeEvent,
    },
};

impl ToFromWasmEvent for SpawnChangeEvent {
    fn to_wasm_event(&self, state: &mut PluginHostState) -> Event {
        let world = state
            .add_world(self.world.clone())
            .expect("failed to add world resource");

        Event::SpawnChangeEvent(SpawnChangeEventData {
            target_world: world,
            previous_position: to_wasm_block_position(self.previous_position),
            previous_yaw: self.previous_yaw,
            previous_pitch: self.previous_pitch,
            new_position: to_wasm_block_position(self.new_position),
            new_yaw: self.new_yaw,
            new_pitch: self.new_pitch,
        })
    }

    fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self {
        match event {
            Event::SpawnChangeEvent(data) => Self {
                world: consume_world(state, &data.target_world),
                previous_position: from_wasm_block_position(data.previous_position),
                previous_yaw: data.previous_yaw,

View on GitHub (pinned to 8d4639e25a)