biomejs/biome · error

{owner} must return Promise

Error message

{owner} must return Promise

What it means

The construct signature (or a validated member's) return type must be a direct reference to `Promise`, not another type. This bail fires in `validate_promise_reference` when the return type node is not a TsReferenceType at all — e.g. a union, an intersection, a literal, or a mapped type. The owner name is interpolated into the message so the offending declaration is identifiable.

Source

Thrown at xtask/codegen/src/generate_global_types/lower.rs:1124

        bail!("Promise constructor executor must not be generic");
    }
    let executor_return_type = regular_return_type(executor.return_type()?, "Promise constructor")?;
    if !matches!(executor_return_type, AnyTsType::TsVoidType(_)) {
        bail!("Promise constructor executor must return void");
    }

    let return_type = member
        .type_annotation()
        .context("Promise constructor is missing a return type")?
        .ty()
        .context("Promise constructor has a malformed return type")?;
    validate_promise_reference(&return_type, "Promise constructor")
}

/// Requires `Promise<...>` without constraining the declaration's projected type argument.
fn validate_promise_reference(type_node: &AnyTsType, owner: &str) -> Result<()> {
    let AnyTsType::TsReferenceType(reference) = type_node else {
        bail!("{owner} must return Promise");
    };
    let name = reference
        .name()
        .with_context(|| format!("{owner} has a missing return type name"))?;
    let biome_js_syntax::AnyTsName::JsReferenceIdentifier(identifier) = name else {
        bail!("{owner} must return Promise");
    };
    if identifier.value_token()?.token_text_trimmed().text() != "Promise" {
        bail!("{owner} must return Promise");
    }
    let type_arguments = reference
        .type_arguments()
        .with_context(|| format!("{owner} must return Promise with one type argument"))?;
    let mut arguments = type_arguments.ts_type_argument_list().into_iter();
    let Some(argument) = arguments.next() else {
        bail!("{owner} must return Promise with one type argument");
    };
    argument.with_context(|| format!("{owner} has a malformed Promise type argument"))?;

View on GitHub (pinned to 3835945f06)

Solutions

  1. Change the return type to a plain `Promise<T>` reference
  2. Remove unions/intersections/modifiers around the return type
  3. If a wrapper type is needed, validate it separately instead of routing it through validate_promise_reference

Example fix

// before
new <T>(executor: ...): Promise<T> | PromiseLike<T>;
// after
new <T>(executor: ...): Promise<T>;
Defensive patterns

Strategy: validation

Validate before calling

// Return type must be a plain reference, not a union/intersection/literal
let ret = "Promise<T>";
assert!(!ret.contains('|') && !ret.contains('&'), "return type must be a bare Promise reference");

Prevention

When it happens

Trigger: Running global-types codegen when a validated declaration's return type is `PromiseLike<T> | Promise<T>`, `readonly Promise<T>`, `T`, or any non-reference type node instead of a plain `Promise<...>` reference.

Common situations: Editing the bundled global declarations and changing a return type to a union or alias; a parser change producing a different AnyTsType variant for what looks like a reference.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of biomejs/biome@3835945f06 (2026-09-13). Data as JSON: /api/errors/d3fb62e321da1d5d. Report an issue: GitHub.