{"record":{"id":"acf4ab728aaa9a7f","repo":"risingwavelabs/risingwave","slug":"return-type-mismatch-expect-actual","errorCode":null,"errorMessage":"return type mismatch, expect: {:?}, actual: {:?}","messagePattern":"return type mismatch, expect: (.+?), actual: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/impl/src/udf/external.rs","lineNumber":75,"sourceCode":"                arrow_schema_udf::Field::new(\"row\", arrow_schema_udf::DataType::Int32, true),\n                to_field(opts.return_type)?,\n            ]\n        } else {\n            vec![to_field(opts.return_type)?]\n        });\n        let function = tokio::task::block_in_place(|| {\n            tokio::runtime::Handle::current().block_on(client.get(&name_in_runtime))\n        })\n        .context(\"failed to check UDF signature\")?;\n        if !data_types_match(&function.args, &args) {\n            bail!(\n                \"argument type mismatch, expect: {:?}, actual: {:?}\",\n                args,\n                function.args,\n            );\n        }\n        if !data_types_match(&function.returns, &returns) {\n            bail!(\n                \"return type mismatch, expect: {:?}, actual: {:?}\",\n                returns,\n                function.returns,\n            );\n        }\n        Ok(CreateFunctionOutput {\n            name_in_runtime,\n            body: None,\n            compressed_binary: None,\n        })\n    },\n    build_fn: |opts| {\n        let link = opts.link.context(\"link is required\")?;\n        let client = get_or_create_flight_client(link)?;\n        Ok(Box::new(ExternalFunction {\n            remote_name: opts.name_in_runtime.to_owned(),\n            client,\n            disable_retry_count: AtomicU8::new(INITIAL_RETRY_COUNT),","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/udf/external.rs#L57-L93","documentation":"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`.","triggerScenarios":"`UdfImplDescriptor::new` when `data_types_match(&function.returns, &returns)` fails — the RETURNS clause in SQL differs from the UDF service's declared return type.","commonSituations":"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.","solutions":["Update the RETURNS clause in CREATE FUNCTION to match the service's return type","Redeploy the UDF service with the return type declared in SQL","Inspect the error's 'actual' field to see the exact service-side type"],"exampleFix":"-- before\nCREATE FUNCTION f(BIGINT) RETURNS INT AS 'f' USING LINK 'http://udf:8815'; -- service returns BIGINT\n-- after\nCREATE FUNCTION f(BIGINT) RETURNS BIGINT AS 'f' USING LINK 'http://udf:8815';","handlingStrategy":"validation","validationCode":"// verify RETURNS clause matches the UDF service's return type before CREATE FUNCTION","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Keep return types stable across UDF versions","Bump function name/version on type changes","Read the 'actual' field of the error for the service-side type"],"tags":["udf","type-mismatch","external-service"],"backgroundTag":"type-mismatch","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}