risingwavelabs/risingwave · error

return type mismatch, expect

Error message

return type mismatch, expect: {:?}, actual: {:?}

What it means

Same signature check as the argument mismatch, but for the return type: the return type declared in `CREATE FUNCTION` must exactly match the return type registered in the external UDF service. Mismatches abort creation of the `UdfImplDescriptor`.

Solutions

  1. Update the RETURNS clause in CREATE FUNCTION to match the service's return type
  2. Redeploy the UDF service with the return type declared in SQL
  3. Inspect the error's 'actual' field to see the exact service-side type

Example fix

-- before
CREATE FUNCTION f(BIGINT) RETURNS INT AS 'f' USING LINK 'http://udf:8815'; -- service returns BIGINT
-- after
CREATE FUNCTION f(BIGINT) RETURNS BIGINT AS 'f' USING LINK 'http://udf:8815';
Defensive patterns

Strategy: validation

Validate before calling

// verify RETURNS clause matches the UDF service's return type before CREATE FUNCTION

Try / catch

try { await rw.query(createFunctionSql) } catch (e) { if (String(e).includes('return type mismatch')) { /* update RETURNS to the 'actual' type in the error */ } else throw e; }

Prevention

When it happens

Trigger: `UdfImplDescriptor::new` when `data_types_match(&function.returns, &returns)` fails — the RETURNS clause in SQL differs from the UDF service's declared return type.

Common situations: UDF changed its return type (e.g. STRING to BINARY, INT to BIGINT) but the SQL DDL still has the old type; nullable/not-null mismatch between service and DDL.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/acf4ab728aaa9a7f. Report an issue: GitHub.

Appendix: source

Thrown at src/expr/impl/src/udf/external.rs:75

                arrow_schema_udf::Field::new("row", arrow_schema_udf::DataType::Int32, true),
                to_field(opts.return_type)?,
            ]
        } else {
            vec![to_field(opts.return_type)?]
        });
        let function = tokio::task::block_in_place(|| {
            tokio::runtime::Handle::current().block_on(client.get(&name_in_runtime))
        })
        .context("failed to check UDF signature")?;
        if !data_types_match(&function.args, &args) {
            bail!(
                "argument type mismatch, expect: {:?}, actual: {:?}",
                args,
                function.args,
            );
        }
        if !data_types_match(&function.returns, &returns) {
            bail!(
                "return type mismatch, expect: {:?}, actual: {:?}",
                returns,
                function.returns,
            );
        }
        Ok(CreateFunctionOutput {
            name_in_runtime,
            body: None,
            compressed_binary: None,
        })
    },
    build_fn: |opts| {
        let link = opts.link.context("link is required")?;
        let client = get_or_create_flight_client(link)?;
        Ok(Box::new(ExternalFunction {
            remote_name: opts.name_in_runtime.to_owned(),
            client,
            disable_retry_count: AtomicU8::new(INITIAL_RETRY_COUNT),

View on GitHub (pinned to 6469eb736d)