{"record":{"id":"5bd7a0ed9ce17b72","repo":"Pumpkin-MC/Pumpkin","slug":"failed-to-add-player-resource-5bd7a0","errorCode":null,"errorMessage":"failed to add player resource","messagePattern":"failed to add player resource","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/player.rs","lineNumber":2369,"sourceCode":"    }\n\n    fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self {\n        match event {\n            Event::PlayerPickupArrowEvent(data) => Self {\n                player: consume_player(state, &data.player),\n                arrow_id: data.arrow_id,\n                cancelled: data.cancelled,\n            },\n            _ => panic!(\"unexpected event type\"),\n        }\n    }\n}\n\nimpl ToFromWasmEvent for PlayerRecipeBookClickEvent {\n    fn to_wasm_event(&self, state: &mut PluginHostState) -> Event {\n        let player = state\n            .add_player(self.player.clone())\n            .expect(\"failed to add player resource\");\n        Event::PlayerRecipeBookClickEvent(PlayerRecipeBookClickEventData {\n            player,\n            recipe_id: self.recipe_id.clone(),\n            make_all: self.make_all,\n            cancelled: self.cancelled,\n        })\n    }\n\n    fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {\n        cleanup_event(&event, state);\n        if let Event::PlayerRecipeBookClickEvent(data) = event {\n            self.cancelled = data.cancelled;\n            self.recipe_id = data.recipe_id;\n            self.make_all = data.make_all;\n        }\n    }\n\n    fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self {","sourceCodeStart":2351,"sourceCodeEnd":2387,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/player.rs#L2351-L2387","documentation":"In `ToFromWasmEvent::to_wasm_event` for `PlayerRecipeBookClickEvent`, `state.add_player(self.player.clone()).expect(\"failed to add player resource\")` panics when the host cannot register the player resource in PluginHostState. add_player returns Option/Result and the expect treats None as unrecoverable: the outgoing WASM event requires a valid player resource handle.","triggerScenarios":"add_player fails to insert/lookup the player — e.g. the player entity no longer exists in host state (left the server mid-event), the resource table is at capacity, or the player key stored on the Rust-side event is stale.","commonSituations":"Player disconnects between event capture and WASM conversion; a plugin race where events queue for players removed from PluginHostState; host bugs clearing the resource table before pending events flush.","solutions":["Check player existence before building the event and skip/consume the event if the player is gone.","Make add_player failure a logged skip rather than a panic (match on None/Err and drop the event).","Clear queued events for a player during disconnect handling so stale events never convert.","Fix any code path that clears PluginHostState while events referencing players are still pending."],"exampleFix":"// before\nlet player = state\n    .add_player(self.player.clone())\n    .expect(\"failed to add player resource\");\n// after\nlet Some(player) = state.add_player(self.player.clone()) else {\n    tracing::warn!(\"skipping PlayerRecipeBookClickEvent: player resource unavailable\");\n    return Event::NoOp; // or skip emission\n};","handlingStrategy":"validation","validationCode":"if !state.contains_player(&self.player) {\n    tracing::warn!(\"player gone; skipping recipe-book-click event\");\n    return;\n}","typeGuard":"fn player_available(state: &PluginHostState, p: &PlayerRef) -> bool { state.contains_player(p) }","tryCatchPattern":"match state.add_player(self.player.clone()) {\n    Ok(player) => build_event(player),\n    Err(e) => tracing::warn!(\"failed to add player resource: {e:?}\"),\n}","preventionTips":["Remove queued events for players at disconnect","Never convert events after PluginHostState teardown","Log add_player failures instead of expecting","Test plugin event pipeline under player connect/disconnect churn"],"tags":["rust","wasm","plugin-api","panic","resource-management","player-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"}