cube-js/cube · error · syn::Error

Return type should be {expected_type}

Error message

Return type should be {expected_type}

What it means

extract_output_for_nested_type unwraps one level of generic arguments (Option or Vec, per `type_to_extract`) from the Result's inner type. If the angle-bracketed argument list is empty — i.e. `Result<>`-like degenerate generics — there is no first generic argument to unwrap, and the helper fails with 'Return type should be {expected_type}' (expected_type is built as e.g. 'Result<Option<_>>').

Source

Thrown at rust/cube/cubesqlplanner/nativebridge/src/lib.rs:288

            Err(syn::Error::new(
                seg.span(),
                "Return type should be Result<_>",
            ))
        }
    }

    fn extract_output_for_nested_type(
        args: &PathArguments,
        type_to_extract: &str,
        expected_type: &str,
    ) -> syn::Result<PathArguments> {
        let error_message = format!("Return type should be {expected_type}");
        match args {
            syn::PathArguments::AngleBracketed(args) => {
                let arg = args
                    .args
                    .first()
                    .ok_or(syn::Error::new(args.span(), error_message.clone()))?;
                match arg {
                    syn::GenericArgument::Type(tp) => match tp {
                        Type::Path(tp) => {
                            let segs = &tp.path.segments;
                            let seg = segs.last().ok_or(syn::Error::new(
                                tp.span(),
                                "Return type should be Result<Option<_>>",
                            ))?;
                            if seg.ident.to_string() == type_to_extract {
                                let args = &seg.arguments;
                                Ok(args.clone())
                            } else {
                                Err(syn::Error::new(seg.span(), error_message.clone()))
                            }
                        }
                        _ => Err(syn::Error::new(arg.span(), error_message.clone())),
                    },
                    _ => Err(syn::Error::new(arg.span(), error_message.clone())),

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Provide the type parameter on the nested wrapper: write `Result<Option<T>>` / `Result<Vec<T>>` fully, never a bare wrapper without generics.
  2. Check for stray angle brackets or removed generics after refactoring.
  3. Compile a minimal example to confirm the exact signature the macro accepts.

Example fix

// before
fn query(&self) -> Result<Option>;

// after
fn query(&self) -> Result<Option<String>>;
Defensive patterns

Strategy: validation

Validate before calling

fn nested_has_type_param(sig: &str) -> bool {
    // e.g. "Result<Option<String>>" — wrapper must carry a type parameter
    sig.contains("Option<") || sig.contains("Vec<")
}
// assert!(nested_has_type_param("Result<Option<String>>"));
// assert!(!nested_has_type_param("Result<Option>")); // would fail

Prevention

When it happens

Trigger: A Result inner type whose generic argument list yields nothing to extract; practically triggered when the nested wrapper has no type parameter the macro can pull out.

Common situations: Degenerate or partially-written generic signatures during refactoring, or mismatched expectations where the developer wrote a wrapper without its type parameter.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/73773c2c2436b692. Report an issue: GitHub.