windmill-labs/windmill · error
Missing otyp for pg arg
Error message
Missing otyp for pg arg
What it means
The PostgreSQL executor builds typed query parameters from the script's argument definitions; each arg needs an `otyp` (the resolved PostgreSQL OID/type name) to convert the JSON value into a typed parameter. When an argument has no `otyp`, conversion cannot proceed and this error aborts the query preparation.
Source
Thrown at backend/windmill-worker/src/pg_executor.rs:486
let mut default_values: HashMap<i32, serde_json::Value> = HashMap::new();
for oidx in arg_indices.iter().sorted() {
if let Some((arg, value)) = param_idx_to_arg_and_value.get(&oidx) {
// Resolve the value: explicit user value > declaration default > NULL.
let value: &serde_json::Value = match (value, arg.default.as_ref()) {
(Some(v), _) => *v,
(None, Some(d)) => default_values.entry(*oidx).or_insert_with(|| d.clone()),
(None, None) => {
if !arg.has_default && !missing_args.contains(&arg.name) {
missing_args.push(arg.name.clone());
}
&serde_json::Value::Null
}
};
let arg_t = arg
.otyp
.as_ref()
.ok_or_else(|| anyhow::anyhow!("Missing otyp for pg arg"))?;
let typ = &arg.typ;
let (param, natural_type) = convert_val(value, arg_t, typ, arg.otyp_inferred)?;
query_params.push(param);
param_meta.push((arg.name.clone(), json_value_kind(value)));
if all_types_resolved {
if otyp_to_pg_type(arg_t).is_ok() {
// The Type comes from convert_val (paired with the binding's
// concrete Rust type) rather than from `otyp_to_pg_type(arg_t)`
// — this prevents the parser-default "text" otyp from
// forcing an assertion that the encoder can't satisfy
// (e.g. Value::Bool with parser-defaulted text → Type::TEXT
// on a Box<bool>).
param_types.push(natural_type);
} else {
all_types_resolved = false;
param_types.clear();
}
}View on GitHub (pinned to e474e8803c)
Solutions
- Open the script's argument definition and set an explicit type for the offending argument so `otyp` resolves.
- Check the arg name in the error path against the script's args; fix missing/renamed parameters in the code and args list.
- If the type is unusual (custom enum, domain), cast it in SQL (`$1::my_type`) or use a supported base type instead.
- Re-save the script through the UI so args are re-validated and otyp is populated; retry the job.
Example fix
// before: arg declared without type in script args
// { "name": "limit", "value": 10 } // no type -> otyp None
// after
// { "name": "limit", "value": 10, "type": "integer" } Defensive patterns
Strategy: validation
Validate before calling
// validate args carry a type before executing the script
for arg in args {
if arg.otyp.is_none() {
return Err(format!("argument '{}' has no resolved type; declare it in the script args", arg.name));
}
} Type guard
fn has_resolved_type(arg: &PgArg) -> bool {
arg.otyp.as_ref().map(|t| !t.is_empty()).unwrap_or(false)
} Prevention
- Always declare a type for every script argument via the UI or API
- Re-save scripts after upgrading Windmill so arg definitions re-resolve
- For custom Postgres types, cast in SQL or use a supported base type
When it happens
Trigger: `do_postgresql_inner` processing args where `arg.otyp` is `None` — the type was never resolved for the parameter (e.g. argument defined without a type in the script UI/resource, or type inference failed and `otyp_inferred` didn't fill it in).
Common situations: Script args edited via the API/CLI omitting the type field; a Postgres type not recognized by Windmill so resolution was skipped; older scripts whose stored args lack otyp after an executor change; passing a null/complex JSON value with no declared parameter type.
Related errors
- result.substring(__RESULT_ERR_PREFIX.length)
- Mixed types in array
- ${scriptPath} declares no S3Object parameter to bind --uploa
- Unimplemented case
- Unsupported database type: ${dbType}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/c11bcc99972ac87b.
Report an issue: GitHub.