{"record":{"id":"397f34e7d32c4c0e","repo":"nushell/nushell","slug":"internal-error-missing-block","errorCode":null,"errorMessage":"internal error: missing block","messagePattern":"internal error: missing block","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/nu-protocol/src/engine/state_working_set.rs","lineNumber":845,"sourceCode":"                decl_id,\n                name.to_vec(),\n                Some(command.description().to_string()),\n                command.command_type(),\n            ));\n        });\n\n        output\n    }\n\n    pub fn get_block(&self, block_id: BlockId) -> &Arc<Block> {\n        let num_permanent_blocks = self.permanent_state.num_blocks();\n        if block_id.get() < num_permanent_blocks {\n            self.permanent_state.get_block(block_id)\n        } else {\n            self.delta\n                .blocks\n                .get(block_id.get() - num_permanent_blocks)\n                .expect(\"internal error: missing block\")\n        }\n    }\n\n    pub fn get_module(&self, module_id: ModuleId) -> &Module {\n        let num_permanent_modules = self.permanent_state.num_modules();\n        if module_id.get() < num_permanent_modules {\n            self.permanent_state.get_module(module_id)\n        } else {\n            self.delta\n                .modules\n                .get(module_id.get() - num_permanent_modules)\n                .expect(\"internal error: missing module\")\n        }\n    }\n\n    pub fn get_block_mut(&mut self, block_id: BlockId) -> &mut Block {\n        let num_permanent_blocks = self.permanent_state.num_blocks();\n        if block_id.get() < num_permanent_blocks {","sourceCodeStart":827,"sourceCodeEnd":863,"githubUrl":"https://github.com/nushell/nushell/blob/2af17cd99edef2c58b3cc95e80224f51ad890eda/crates/nu-protocol/src/engine/state_working_set.rs#L827-L863","documentation":"Panic inside nushell's StateWorkingSet::get_block: a BlockId was passed that is >= the permanent block count but does not index into the working set's delta blocks. Block IDs are assigned as permanent blocks first, then delta blocks appended during the current compile session; a lookup that falls off both ranges means the ID is stale or from a different working set. This is an engine-internal invariant ('internal error') and always indicates a bug in nushell or a misused working set, not bad user input.","triggerScenarios":"Calling get_block with a BlockId captured before a merge_delta/reset (IDs shift when the delta is merged and the delta list empties), or using an ID obtained from a different EngineState/StateWorkingSet instance; also any parser code path that computes a BlockId without going through add_block.","commonSituations":"Contributing parser/evaluator changes to nushell that cache DeclId/BlockId across working-set lifetimes, plugins or embedders that hold engine IDs while the engine state advances, or a refactor that reorders block registration during compile.","solutions":["If you hit this as a user, update nushell and file the reproducing script — the message literally says 'internal error', user code cannot trigger it through valid input","If embedding nushell, always re-resolve BlockIds in the same StateWorkingSet that created them; never reuse IDs after EngineState::merge_delta","Check bounds before lookup: block_id.get() < permanent_state.num_blocks() + delta.blocks.len()","As a maintainer, replace expect with a proper ShellError (or debug_assert + fallback) so the engine reports the bad ID instead of panicking"],"exampleFix":"// before (parser/embedder code holding a stale id)\nlet block = working_set.get_block(old_block_id);\n\n// after: re-resolve the block in the working set that owns it\nlet block_id = working_set.add_block(block);\nlet block = working_set.get_block(block_id);","handlingStrategy":"validation","validationCode":"fn block_in_range(ws: &StateWorkingSet, id: BlockId) -> bool {\n    let n = ws.permanent_state.num_blocks() + ws.delta.blocks.len();\n    id.get() < n\n}","typeGuard":"fn owns_block_id(ws: &StateWorkingSet, id: BlockId) -> bool {\n    id.get() >= ws.permanent_state.num_blocks()\n        && id.get() - ws.permanent_state.num_blocks() < ws.delta.blocks.len()\n        || id.get() < ws.permanent_state.num_blocks()\n}","tryCatchPattern":null,"preventionTips":["Never cache BlockIds across EngineState::merge_delta calls; re-resolve after merges","Only use IDs minted by the same StateWorkingSet that performs the lookup","In embedded usage, wrap engine calls so unexpected panics surface as reportable bugs rather than crashes"],"tags":["nushell","rust","panic","expect","engine-internals","block-id"],"backgroundTag":"stale-id-lookup","analyzedSha":"2af17cd99edef2c58b3cc95e80224f51ad890eda","analyzedAt":"2026-08-17T16:24:07.527Z","contentChangedAt":"2026-08-17T16:24:07.527Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}