windmill-labs/windmill · error

typed records are not supported, use `ident: record`

Error message

typed records are not supported, use `ident: record`

What it means

Nushell allows typed records like `record<name: string>` as parameter types. Windmill's parser supports the bare `record` type only (mapped to an open object); any type string containing `record<` is rejected with guidance to use the untyped form. The structured inner schema cannot be represented in Windmill's ObjectType here.

Source

Thrown at backend/parsers/windmill-parser-nu/src/lib.rs:183

            "number" => Typ::Float,
            "record" => Typ::Object(ObjectType::new(None, Some(vec![]))),
            "table" => Typ::List(Box::new(Typ::Object(ObjectType::new(None, Some(vec![]))))),
            "nothing" => Typ::Unknown,
            // TODO: needs additional work on literal parsing
            // "binary" => Typ::Bytes,
            "datetime" => Typ::Datetime,
            "any" => Typ::Unknown,
            "bool" => Typ::Bool,
            // Lists
            "list" | "list<any>" | "list<nothing>" => Typ::List(Box::new(Typ::Unknown)),
            "list<number>" => Typ::List(Box::new(Typ::Float)),
            "list<bool>" => Typ::List(Box::new(Typ::Bool)),
            "list<string>" => Typ::List(Box::new(Typ::Str(None))),
            // list<float/int> is not supported
            // Records and Tables
            // TODO: Support in V1?
            s if s.contains("record<") => {
                bail!("typed records are not supported, use `ident: record`")
            }
            s if s.contains("table<") => {
                bail!("typed tables are not supported, use `ident: table`")
            }
            s => bail!("{s} is not supported"),
        };
        Ok(typ)
    }
    fn parse_default(
        content: &str,
        ctx: &[&str],
        i: usize,
        skip: &mut usize,
    ) -> anyhow::Result<Value> {
        let mut c = content.replace("=", "").trim().to_owned();

        fn parse_object_literal(
            (open, close): (char, char),

View on GitHub (pinned to e474e8803c)

Solutions

  1. Change the type to plain `record` and validate fields inside the script body.
  2. Use `any` and parse/validate the incoming JSON object manually in the script.
  3. Model the structured data as a Windmill resource or a JSON object input via a `dict`-like convention if the platform offers it.
  4. Keep the typed record for local use but strip the type parameters in the deployed `def main` signature.

Example fix

// before
def main [user: record<name: string, age: int>]
// after
def main [user: record] {
  let name = $user.name? | default ""
}
Defensive patterns

Strategy: validation

Validate before calling

function validateNoTypedRecords(code) {
  const m = code.match(/def\s+main\s*\[([^\]]*)\]/);
  if (m && /record</.test(m[1])) return 'typed records (record<...>) are not supported; use bare `record` and validate fields in the body';
  return null;
}

Try / catch

try {
  const sig = parseNuSignature(source);
} catch (e) {
  if (String(e).includes('typed records are not supported')) {
    throw new Error('Change the annotation to `record` and validate inner fields inside the script');
  }
  throw e;
}

Prevention

When it happens

Trigger: Declaring `def main [cfg: record<name: string, retries: int>]` — any parameter type containing `record<` — when the signature is parsed.

Common situations: Modern Nushell scripts using typed record annotations for IDE/validation benefits being deployed to Windmill; upgrading Nushell and adopting typed records in existing scripts.

Related errors


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