nushell/nushell · error

Attribute block must contain at least one attribute

Error message

Attribute block must contain at least one attribute

What it means

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.

Source

Thrown at crates/nu-parser/src/parse_def.rs:291

            },
        );
    }

    Expression::new(working_set, Expr::Call(call), call_span, Type::Nothing)
}

pub fn parse_attribute_block(
    working_set: &mut StateWorkingSet,
    lite_command: &LiteCommand,
) -> Pipeline {
    let attributes = lite_command
        .attribute_commands()
        .map(|cmd| parse_attribute(working_set, &cmd).0)
        .collect::<Vec<_>>();

    let last_attr_span = attributes
        .last()
        .expect("Attribute block must contain at least one attribute")
        .expr
        .span;

    working_set.error(ParseError::AttributeRequiresDefinition(last_attr_span));
    let cmd_span = if lite_command.command_parts().is_empty() {
        last_attr_span.past()
    } else {
        Span::concat(lite_command.command_parts())
    };
    let cmd_expr = garbage(working_set, cmd_span);
    let ty = cmd_expr.ty.clone();

    let attr_block_span = Span::merge_many(
        attributes
            .first()
            .map(|x| x.expr.span)
            .into_iter()
            .chain(Some(cmd_span)),

View on GitHub (pinned to 8e03210652)

Solutions

  1. Only call parse_attribute_block when lite_command.attribute_commands().next().is_some()
  2. Use the normal public parse entry points so the lite parser decides routing
  3. Sync forked parser code with the nu-parser version you depend on
  4. Report upstream if plain source input (an '@attr' line followed by nothing) triggers it via the standard path

Example fix

// before: assumes the command has attributes
let pipeline = parse_attribute_block(working_set, &lite_command);

// after: only enter the attribute-block path when attributes exist
if lite_command.attribute_commands().next().is_some() {
    let pipeline = parse_attribute_block(working_set, &lite_command);
} else {
    // fall through to regular command parsing
}
Defensive patterns

Strategy: validation

Validate before calling

if lite_command.attribute_commands().next().is_some() {
    let pipeline = parse_attribute_block(working_set, &lite_command);
} else {
    // regular command path — parse_attribute_block would panic
}

Type guard

fn has_attributes(cmd: &LiteCommand) -> bool {
    cmd.attribute_commands().next().is_some()
}

Prevention

When it happens

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

Common situations: Forked or patched lite parsers; embedders wiring parse stages manually; tests that fabricate LiteCommand structs instead of parsing source text.

Related errors


AI-assisted analysis of nushell/nushell@8e03210652 (2026-08-17). Data as JSON: /api/errors/0bac7b80c5c249e6. Report an issue: GitHub.