{"record":{"id":"61f2b222922673a3","repo":"risingwavelabs/risingwave","slug":"argument-type-mismatch-expect-actual","errorCode":null,"errorMessage":"argument type mismatch, expect: {:?}, actual: {:?}","messagePattern":"argument type mismatch, expect: (.+?), actual: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/impl/src/udf/external.rs","lineNumber":68,"sourceCode":"            opts.arg_types\n                .iter()\n                .map(to_field)\n                .try_collect::<Fields>()?,\n        );\n        let returns = arrow_schema_udf::Schema::new(if opts.kind.is_table() {\n            vec![\n                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    },","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/udf/external.rs#L50-L86","documentation":"When creating an external UDF, the frontend fetches the function's signature from the external UDF service and compares it with the argument types declared in `CREATE FUNCTION`. If the protobuf-declared arg types do not match the SQL-declared ones, the descriptor construction fails so the mismatch surfaces before execution.","triggerScenarios":"`UdfImplDescriptor::new` (via `CreateFunctionOutput`) when the external UDF server's reported `function.args` differ from the `args` given in the CREATE FUNCTION statement, as checked by `data_types_match`.","commonSituations":"UDF service was rebuilt with different signatures while the SQL definition was not updated; wrong order of arguments in CREATE FUNCTION; type drift (e.g. INT vs BIGINT, nullable vs not) between arrow schema and SQL types.","solutions":["Compare the reported `actual` (service) types with the SQL `CREATE FUNCTION ... AS ...` argument types and align them","Update the CREATE FUNCTION statement to match the UDF service's registered signature","Rebuild/redeploy the UDF service with the expected signature if it drifted"],"exampleFix":"-- before\nCREATE FUNCTION f(INT) RETURNS INT AS 'f' USING LINK 'http://udf:8815'; -- service expects (BIGINT)\n-- after\nCREATE FUNCTION f(BIGINT) RETURNS INT AS 'f' USING LINK 'http://udf:8815';","handlingStrategy":"validation","validationCode":"// compare CREATE FUNCTION arg types against the UDF service's registered signature before deploying","typeGuard":null,"tryCatchPattern":"try { await rw.query(createFunctionSql) } catch (e) { if (String(e).includes('argument type mismatch')) { /* fetch /info from UDF service and align DDL */ } else throw e; }","preventionTips":["Regenerate CREATE FUNCTION DDL from the UDF service's declared signature","Keep UDF service and DDL in the same repo/version","Run signature checks in CI"],"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"}