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

Return type should be Result<Option<_>>

Error message

Return type should be Result<Option<_>>

What it means

When unwrapping the nested wrapper inside Result, extract_output_for_nested_type checks that the inner path's last segment is the expected wrapper (type_to_extract, e.g. Option). If the inner type is not that wrapper — e.g. `Result<Vec<T>>` where `Result<Option<T>>` was expected — it fails with the hardcoded message 'Return type should be Result<Option<_>>'.

Source

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

    }

    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())),
                }
            }
            _ => Err(syn::Error::new(args.span(), error_message.clone())),
        }
    }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Make the inner type exactly match the expected wrapper: use `Result<Option<T>>` when the output is optional.
  2. If a Vec is intended, ensure the method is declared as `Result<Vec<T>>` and that no optional-flag mismatch exists in the surrounding signature.
  3. Avoid aliases or deeply nested wrappers; write the canonical shape directly.

Example fix

// before
fn find(&self) -> Result<Vec<String>>; // macro expects Result<Option<_>> here

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

Strategy: validation

Validate before calling

fn matches_expected_shape(sig: &str, wrapper: &str) -> bool {
    let expected = format!("Result<{}<", wrapper);
    sig.contains(&expected)
}
// assert!(matches_expected_shape("fn f() -> Result<Option<String>>;", "Option"));
// assert!(!matches_expected_shape("fn f() -> Result<Vec<String>>;", "Option"));

Prevention

When it happens

Trigger: A bridged method with an optional output declared as `fn f(&self) -> Result<String>;` or `-> Result<Vec<String>>;` while the macro's optional flag is set (expected shape Result<Option<_>>).

Common situations: Mismatch between the helper flags/how the output was detected as optional and the actual written return type; changing Option to Vec (or removing it) after the signature was already validated for Option.

Related errors


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