{"record":{"id":"8a3303b14202b5c6","repo":"windmill-labs/windmill","slug":"document-must-be-a-string","errorCode":null,"errorMessage":"Document must be a string","messagePattern":"Document must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"windmill-yaml-validator/src/validation/yaml-validator.ts","lineNumber":113,"sourceCode":"    this.validateFlow = ajv.getSchema(\"#/components/schemas/OpenFlow\")!;\n    this.validateSchedule = ajv.compile(scheduleSchema as AnySchema);\n\n    this.validateTrigger = Object.fromEntries(\n      SUPPORTED_TRIGGER_KINDS.map((kind) => [kind, ajv.compile(TRIGGER_SCHEMAS[kind])])\n    ) as Record<TriggerKind, ValidateFunction>;\n  }\n\n  /**\n   * Validates a Windmill YAML document based on the selected target.\n   * @param doc - The YAML document as string\n   * @param target - Which Windmill schema to validate against\n   */\n  validate(\n    doc: string,\n    target: ValidationTarget\n  ): { parsed: YamlParserResult<unknown>; errors: ErrorObject[] } {\n    if (typeof doc !== \"string\") {\n      throw new Error(\"Document must be a string\");\n    }\n\n    const parsed = parseWithPointers(doc);\n    const { data } = parsed;\n\n    let validator: ValidateFunction;\n    if (target.type === \"flow\") {\n      validator = this.validateFlow;\n    } else if (target.type === \"schedule\") {\n      validator = this.validateSchedule;\n    } else {\n      validator = this.validateTrigger[target.triggerKind];\n      if (!validator) {\n        throw new Error(`Unsupported trigger kind: ${target.triggerKind}`);\n      }\n    }\n\n    const ok = validator(data);","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/windmill-yaml-validator/src/validation/yaml-validator.ts#L95-L131","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass the raw YAML text (fs.readFileSync(path, 'utf8'))","If you have an object, YAML.stringify it first","Log/typeof-check the doc argument to confirm it is a string"],"exampleFix":"// before\nvalidator.validate(fs.readFileSync('flow.yaml'), { type: 'flow' })\n// after\nvalidator.validate(fs.readFileSync('flow.yaml', 'utf8'), { type: 'flow' })","handlingStrategy":"type-guard","validationCode":"if (typeof doc !== 'string') throw new TypeError('YAML document must be a raw string, got ' + typeof doc);","typeGuard":"function isString(v: unknown): v is string { return typeof v === 'string'; }","tryCatchPattern":"try {\n  validator.validate(doc, target);\n} catch (e) {\n  if (e.message === 'Document must be a string') {\n    throw new Error('Pass raw YAML text (utf8 string), not a parsed object or Buffer');\n  }\n  throw e;\n}","preventionTips":["Read files with encoding: fs.readFileSync(path, 'utf8')","Never pass an already-parsed object to a text-based validator","Assert typeof doc === 'string' at the call site during development"],"tags":["yaml","validation","type-error"],"backgroundTag":"invalid-argument-value","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}