{"record":{"id":"0bac7b80c5c249e6","repo":"nushell/nushell","slug":"attribute-block-must-contain-at-least-one-attribut","errorCode":null,"errorMessage":"Attribute block must contain at least one attribute","messagePattern":"Attribute block must contain at least one attribute","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/nu-parser/src/parse_def.rs","lineNumber":291,"sourceCode":"            },\n        );\n    }\n\n    Expression::new(working_set, Expr::Call(call), call_span, Type::Nothing)\n}\n\npub fn parse_attribute_block(\n    working_set: &mut StateWorkingSet,\n    lite_command: &LiteCommand,\n) -> Pipeline {\n    let attributes = lite_command\n        .attribute_commands()\n        .map(|cmd| parse_attribute(working_set, &cmd).0)\n        .collect::<Vec<_>>();\n\n    let last_attr_span = attributes\n        .last()\n        .expect(\"Attribute block must contain at least one attribute\")\n        .expr\n        .span;\n\n    working_set.error(ParseError::AttributeRequiresDefinition(last_attr_span));\n    let cmd_span = if lite_command.command_parts().is_empty() {\n        last_attr_span.past()\n    } else {\n        Span::concat(lite_command.command_parts())\n    };\n    let cmd_expr = garbage(working_set, cmd_span);\n    let ty = cmd_expr.ty.clone();\n\n    let attr_block_span = Span::merge_many(\n        attributes\n            .first()\n            .map(|x| x.expr.span)\n            .into_iter()\n            .chain(Some(cmd_span)),","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/nushell/nushell/blob/8e03210652f3c48c4521cec982d96e4cb6c67181/crates/nu-parser/src/parse_def.rs#L273-L309","documentation":"parse_attribute_block handles a run of '@attr' lines that is not followed by a definition; it exists mainly to emit ParseError::AttributeRequiresDefinition. Before building that error it asserts attributes.last() is Some, i.e. the LiteCommand carried at least one attribute command. It panics only when invoked on a LiteCommand whose attribute_commands() iterator is empty, breaking the contract that only attribute-carrying commands reach this function.","triggerScenarios":"Calling parse_attribute_block with a LiteCommand that has no attribute commands; a lite-parser regression that routes a plain command into the attribute-block path; hand-constructed LiteCommand values with an empty attribute list.","commonSituations":"Forked or patched lite parsers; embedders wiring parse stages manually; tests that fabricate LiteCommand structs instead of parsing source text.","solutions":["Only call parse_attribute_block when lite_command.attribute_commands().next().is_some()","Use the normal public parse entry points so the lite parser decides routing","Sync forked parser code with the nu-parser version you depend on","Report upstream if plain source input (an '@attr' line followed by nothing) triggers it via the standard path"],"exampleFix":"// before: assumes the command has attributes\nlet pipeline = parse_attribute_block(working_set, &lite_command);\n\n// after: only enter the attribute-block path when attributes exist\nif lite_command.attribute_commands().next().is_some() {\n    let pipeline = parse_attribute_block(working_set, &lite_command);\n} else {\n    // fall through to regular command parsing\n}","handlingStrategy":"validation","validationCode":"if lite_command.attribute_commands().next().is_some() {\n    let pipeline = parse_attribute_block(working_set, &lite_command);\n} else {\n    // regular command path — parse_attribute_block would panic\n}","typeGuard":"fn has_attributes(cmd: &LiteCommand) -> bool {\n    cmd.attribute_commands().next().is_some()\n}","tryCatchPattern":null,"preventionTips":["Only enter attribute-block handling through the lite parser's own routing","In tests, build LiteCommands via lite-parsing real source text instead of struct literals","Keep any forked lite-parser classification logic in lockstep with parse_def.rs expectations"],"tags":["rust","panic","nu-parser","attributes","lite-parser","internal-invariant"],"backgroundTag":"internal-invariant-panic","analyzedSha":"8e03210652f3c48c4521cec982d96e4cb6c67181","analyzedAt":"2026-08-17T16:24:07.527Z","contentChangedAt":"2026-08-17T16:24:07.527Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}