windmill-labs/windmill · error

Cannot parse '{s}' as real: {e}

Error message

Cannot parse '{s}' as real: {e}

What it means

convert_val converts a JSON string argument into a tokio-postgres ToSql parameter. When the declared arg type is real/float4, it parses the string with f32::from_str and wraps any ParseFloatError as 'Cannot parse ... as real'. It exists so a valid FLOAT4 parameter is built client-side rather than failing server-side with a type mismatch.

Source

Thrown at backend/windmill-worker/src/pg_executor.rs:1648

        Value::String(s) if arg_t == "bytea" => {
            let bytes = engine::general_purpose::STANDARD
                .decode(s)
                .unwrap_or(vec![]);
            Ok((Box::new(bytes), Type::BYTEA))
        }
        // Parse Strings into the matching native Rust type for the remaining
        // recognised arg_ts that didn't have a dedicated arm. Without these,
        // a string value lands in the generic Value::String fallback below
        // (Box<String> + TEXT) and the server-side comparison
        // `<numeric|real|...> = text` fails since PG has no implicit cast.
        Value::String(s) if arg_t == "numeric" || arg_t == "decimal" => s
            .parse::<Decimal>()
            .map(|d| (Box::new(d) as Box<dyn ToSql + Sync + Send>, Type::NUMERIC))
            .map_err(|e| anyhow::anyhow!("Cannot parse '{s}' as numeric: {e}").into()),
        Value::String(s) if arg_t == "real" || arg_t == "float4" => s
            .parse::<f32>()
            .map(|n| (Box::new(n) as Box<dyn ToSql + Sync + Send>, Type::FLOAT4))
            .map_err(|e| anyhow::anyhow!("Cannot parse '{s}' as real: {e}").into()),
        Value::String(s)
            if arg_t == "double" || arg_t == "double precision" || arg_t == "float8" =>
        {
            s.parse::<f64>()
                .map(|n| (Box::new(n) as Box<dyn ToSql + Sync + Send>, Type::FLOAT8))
                .map_err(|e| anyhow::anyhow!("Cannot parse '{s}' as double: {e}").into())
        }
        Value::String(s) if arg_t == "oid" => s
            .parse::<u32>()
            .map(|n| (Box::new(n) as Box<dyn ToSql + Sync + Send>, Type::OID))
            .map_err(|e| anyhow::anyhow!("Cannot parse '{s}' as oid: {e}").into()),
        Value::String(s) if arg_t == "bool" || arg_t == "boolean" => {
            // Accept the same literals Postgres' boolin() does.
            let b = match s.to_ascii_lowercase().as_str() {
                "true" | "t" | "yes" | "y" | "1" | "on" => true,
                "false" | "f" | "no" | "n" | "0" | "off" => false,
                _ => {
                    return Err(

View on GitHub (pinned to e474e8803c)

Solutions

  1. Ensure the string is a valid float literal (dot separator) before invoking the step
  2. Replace the locale decimal comma with a dot and trim whitespace/units
  3. If values are non-finite or need more precision, switch the arg/column to double/float8 or numeric
  4. Pass as a JSON number instead of a string when precision allows

Example fix

// before
args: { temperature: "21,4" }  // comma decimal -> ParseFloatError
// after
args: { temperature: "21.4" }
Defensive patterns

Strategy: validation

Validate before calling

function assertFloat4(s) {
  const n = Number(typeof s === "string" ? s.trim().replace(",", ".") : s);
  if (Number.isNaN(n) || !Number.isFinite(n)) {
    throw new Error(`value ${JSON.stringify(s)} is not a valid float4`);
  }
}

Type guard

const isFloat4 = (v) => Number.isFinite(Number(typeof v === "string" ? v.trim().replace(",", ".") : v));

Try / catch

try {
  await runPgStep(args);
} catch (e) {
  if (String(e.message).includes("as real")) {
    throw new Error(`Argument must be a finite f32 literal, got: ${e.message}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: A step argument typed real/float4 receives a Value::String that is not a valid f32 ('1,5', 'abc', '', 'Infinity', or a hex float).

Common situations: Comma decimal separators from European locales; sensor data with units attached ('3.2 kg'); empty form fields; values that legitimately need double precision being forced into float4 with precision loss complaints.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/7523b2ea3b52cce0. Report an issue: GitHub.