windmill-labs/windmill · error · Error

Document must be a string

Error message

Document must be a string

What it means

The YAML validator's validate() method in windmill-yaml-validator/src/validation/yaml-validator.ts:113 requires the document to be a string, since it feeds it to parseWithPointers for source-mapped diagnostics. It throws "Document must be a string" when anything else (object, Buffer, undefined) is passed.

Source

Thrown at windmill-yaml-validator/src/validation/yaml-validator.ts:113

    this.validateFlow = ajv.getSchema("#/components/schemas/OpenFlow")!;
    this.validateSchedule = ajv.compile(scheduleSchema as AnySchema);

    this.validateTrigger = Object.fromEntries(
      SUPPORTED_TRIGGER_KINDS.map((kind) => [kind, ajv.compile(TRIGGER_SCHEMAS[kind])])
    ) as Record<TriggerKind, ValidateFunction>;
  }

  /**
   * Validates a Windmill YAML document based on the selected target.
   * @param doc - The YAML document as string
   * @param target - Which Windmill schema to validate against
   */
  validate(
    doc: string,
    target: ValidationTarget
  ): { parsed: YamlParserResult<unknown>; errors: ErrorObject[] } {
    if (typeof doc !== "string") {
      throw new Error("Document must be a string");
    }

    const parsed = parseWithPointers(doc);
    const { data } = parsed;

    let validator: ValidateFunction;
    if (target.type === "flow") {
      validator = this.validateFlow;
    } else if (target.type === "schedule") {
      validator = this.validateSchedule;
    } else {
      validator = this.validateTrigger[target.triggerKind];
      if (!validator) {
        throw new Error(`Unsupported trigger kind: ${target.triggerKind}`);
      }
    }

    const ok = validator(data);

View on GitHub (pinned to e474e8803c)

Solutions

  1. Pass the raw YAML text (fs.readFileSync(path, 'utf8'))
  2. If you have an object, YAML.stringify it first
  3. Log/typeof-check the doc argument to confirm it is a string

Example fix

// before
validator.validate(fs.readFileSync('flow.yaml'), { type: 'flow' })
// after
validator.validate(fs.readFileSync('flow.yaml', 'utf8'), { type: 'flow' })
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof doc !== 'string') throw new TypeError('YAML document must be a raw string, got ' + typeof doc);

Type guard

function isString(v: unknown): v is string { return typeof v === 'string'; }

Try / catch

try {
  validator.validate(doc, target);
} catch (e) {
  if (e.message === 'Document must be a string') {
    throw new Error('Pass raw YAML text (utf8 string), not a parsed object or Buffer');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling validator.validate(doc, target) with a non-string doc — e.g. an already-parsed JS object, a Buffer from fs.readFileSync, or undefined when a file read failed.

Common situations: Passing the result of YAML.parse to a validator that expects raw text; reading a file without .toString('utf8'); a variable being undefined because the file didn't load.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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