oxc-project/oxc · warning

Cannot create a `NonEmptyStack` from an empty iterator

Error message

Cannot create a `NonEmptyStack` from an empty iterator

What it means

Diagnostic from oxlint's vue/require-render-return rule (crates/oxc_linter/src/rules/vue/require_render_return.rs). A `render` function on a Vue component options object must return a value on every code path — Vue invokes it to produce the VNode tree, and a falling-through path yields `undefined`, rendering nothing. The rule runs control-flow analysis (definitely_returns_in_all_codepaths) over the render body.

Source

Thrown at crates/oxc_data_structures/src/stack/non_empty.rs:564

    /// No allocation occurs, and no data is copied.
    #[inline]
    fn from(stack: NonEmptyStack<T>) -> Self {
        stack.into_vec()
    }
}

impl<T> FromIterator<T> for NonEmptyStack<T> {
    /// Create a [`NonEmptyStack`] from an iterator.
    ///
    /// # Panics
    /// Panics if the iterator is empty.
    #[inline] // It mostly just delegates to `Vec::from_iter`
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        // Collect into a `Vec` first, because `Vec` has specialized implementations of `from_iter`
        // for various kinds of iterators, which we cannot replicate in stable Rust
        let vec = Vec::from_iter(iter);
        Self::try_from(vec)
            .unwrap_or_else(|_| panic!("Cannot create a `NonEmptyStack` from an empty iterator"))
    }
}

// SAFETY: `NonEmptyStack<T>` can be `Send` / `Sync` if `T` is `Send` / `Sync`.
// It does not use interior mutability, and is essentially the same as `Vec<T>` in this respect,
// which implements `Send` / `Sync` in the same way.
unsafe impl<T: Send> Send for NonEmptyStack<T> {}
// SAFETY: See above.
unsafe impl<T: Sync> Sync for NonEmptyStack<T> {}

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! assert_len_cap_last {
        ($stack:ident, $len:expr, $capacity:expr, $last:expr) => {
            assert_eq!($stack.len(), $len);
            assert_eq!($stack.capacity(), $capacity);

View on GitHub (pinned to 36ec0ef2ba)

Solutions

  1. Add a fallback `return h('div')` (or another VNode) after the conditionals so every path returns.
  2. Return `null` for intentionally empty states — still a value, and Vue renders a placeholder.
  3. Restructure to a single return: `return this.show ? h('div', this.msg) : h('span')`.

Example fix

// before
render() {
  if (this.show) return h('div', this.msg);
}

// after
render() {
  if (this.show) return h('div', this.msg);
  return h('div');
}
Defensive patterns

Strategy: validation

Validate before calling

// warn when a render function body has no return statement
const fn = source.slice(source.indexOf('render('));
const body = fn.slice(0, fn.indexOf('}'));
if (body && !/\breturn\b/.test(body)) {
  throw new Error('render() must return a VNode on every path');
}

Prevention

When it happens

Trigger: A `render()` method (or `render: function` / arrow form) on a Vue component options object where some branch lacks a return, e.g. `render(h) { if (this.x) return h('div'); }` — the implicit else path returns undefined. Returns inside callbacks (forEach, map callbacks) do not count as returns of the render function itself.

Common situations: Hand-written render functions and JSX components; early returns added during debugging then forgotten; refactors that move the return into a nested callback.

Related errors


AI-assisted analysis of oxc-project/oxc@36ec0ef2ba (2026-08-20). Data as JSON: /api/errors/6532284cf2c12e7a. Report an issue: GitHub.