nushell/nushell · error
Already checked that is a series
Error message
Already checked that is a series
What it means
Panic in NuDataFrame::computed_binary_op (operations.rs): in the (true, true) arm after matching (self.is_series(), rhs.is_series()), the lhs is converted with as_series(lhs_span).expect('Already checked that is a series'). Since the match arm only runs when both frames have width 1, as_series cannot take its error branch and first() cannot be None — the expect documents the match/lookup agreement and is effectively unreachable.
Source
Thrown at crates/nu_plugin_polars/src/dataframe/values/nu_dataframe/operations.rs:37
impl NuDataFrame {
pub fn compute_with_value(
&self,
plugin: &PolarsPlugin,
lhs_span: Span,
operator: Operator,
op_span: Span,
right: &Value,
) -> Result<NuDataFrame, ShellError> {
let rhs_span = right.span();
match right {
Value::Custom { .. } => {
let rhs = NuDataFrame::try_from_value_coerce(plugin, right, rhs_span)?;
match (self.is_series(), rhs.is_series()) {
(true, true) => {
let lhs = &self
.as_series(lhs_span)
.expect("Already checked that is a series");
let rhs = &rhs
.as_series(rhs_span)
.expect("Already checked that is a series");
if lhs.dtype() != rhs.dtype() {
return Err(ShellError::IncompatibleParameters {
left_message: format!("datatype {}", lhs.dtype()),
left_span: lhs_span,
right_message: format!("datatype {}", lhs.dtype()),
right_span: rhs_span,
});
}
if lhs.len() != rhs.len() {
return Err(ShellError::IncompatibleParameters {
left_message: format!("len {}", lhs.len()),
left_span: lhs_span,
right_message: format!("len {}", rhs.len()),View on GitHub (pinned to 8e03210652)
Solutions
- Keep is_series() and as_series()'s width check derived from the same predicate when refactoring
- Avoid sharing/mutating NuDataFrame values across threads during operations
- Consider replacing the expects in the (true, true) arm with error returns to future-proof
Example fix
// before
let lhs = &self.as_series(lhs_span).expect("Already checked that is a series");
// after: reuse the already-checked width directly
let lhs = self.df.get_columns()[0].as_materialized_series(); Defensive patterns
Strategy: validation
Validate before calling
// ensure both operands are single-column before the operation
if !(lhs.is_series() && rhs.is_series()) {
return Err(ShellError::IncompatibleParametersSingle { msg: "operands must be single-column dataframes".into(), span: lhs_span });
} Type guard
fn both_series(l: &NuDataFrame, r: &NuDataFrame) -> bool {
l.is_series() && r.is_series()
} Prevention
- Select a single column from each side before dataframe arithmetic
- Avoid mutating the operands concurrently during the operation
- Report a hit upstream — user pipelines cannot legitimately trigger it
When it happens
Trigger: Divergence between the is_series() predicate and as_series()'s internal check (e.g. someone changes one without the other), or concurrent mutation of either dataframe between the match and the conversion.
Common situations: Only relevant to nu_plugin_polars maintainers/refactors; users performing arithmetic between single-column dataframes get real ShellErrors (IncompatibleParameters, etc.) long before this can matter.
Related errors
- We have already checked that the width is 1
- already checked there is a value
- Already checked that dataframes have same columns
- Lazy expression must not be empty to apply
- there should always be a frame
AI-assisted analysis of nushell/nushell@8e03210652 (2026-08-17).
Data as JSON: /api/errors/40bfe9b9913f637e.
Report an issue: GitHub.