# yaml:lint

The yaml:lint collect plugin reports which of its input files fail to parse as YAML. It makes no assertion about the contents of those files — that is yaml:key's job. Use it as a cheap syntax gate over a directory of config.

It is the 1.x replacement for the 0.x yamllint check.

# Why this plugin exists

Everywhere else in the pipeline a YAML parse failure is a collection error, and any collection error is fatal — the run aborts with failed to collect facts before reaching the analyse stage. That makes the exact condition yamllint reports impossible to express by composing other plugins.

yaml:lint inverts that relationship: a parse failure becomes ordinary data, so an analyser can act on it. This is why the plugin never raises a collection error for a malformed document — the only error it raises is genuine misconfiguration (an unusable input format), which should still be fatal.

# Inputs

yaml:lint accepts a single input in map-bytes format — a map of filename to file contents.

Accepted format Typical source
map-bytes file:lookup with file-names-only: false

File selection is deliberately delegated to file:lookup, so path, pattern, exclude-pattern and skip-dirs are inherited rather than reimplemented.

`file-names-only` must be false

file:lookup defaults to emitting file names (list-string). yaml:lint needs file contents, so set file-names-only: false. Leaving it at the default is rejected at input validation with inputFormat 'list-string' not supported for 'yaml:lint'.

# Plugin fields

Field Description Required Default
all-documents Validate every document in a multi-document (----separated) file rather than only the first. No true

# Common fields

Field Description Required Default
name The name/identifier of the plugin - this is the yaml key in the config file when defining the fact. Yes -
connection The connection to use for collecting the fact. No ""
input A previous input to use when collecting the fact. No ""
additional-inputs Additional previous inputs to use when collecting the fact. No []

# Return format

map-string — a map of filename to parse error, containing only the files that failed. A tree with no invalid files emits an empty map (not nil), so not:empty sees a supported format and simply finds nothing to report.

Pair it with not:empty, which breaches once per entry.

# Example

collect:
  config-files:
    file:lookup:
      path: config/default
      pattern: '.*\.yml$'
      file-names-only: false

  invalid-yaml:
    yaml:lint:
      input: config-files

analyse:
  yaml-is-valid:
    not:empty:
      description: 'YAML files that do not parse'
      input: invalid-yaml
      severity: high
      breach-format:
        type: key-value
        key-label: file
        key: '{{ .Breach.Key }}'
        value-label: 'YAML error'
        value: '{{ .Breach.Value }}'

Source: examples/yaml-lint.yml — see also the yamllint recipe.

# Error classification

Two message shapes are distinguished, preserving the 0.x breach labels:

Condition Message
A yaml.TypeError — the document parsed but contained conflicts, e.g. a duplicate key cannot decode yaml: <details>
Any other parse failure — typically a syntax error The parser message verbatim, e.g. yaml: line 3: did not find expected ',' or ']'

A TypeError can aggregate several complaints; all are joined with ; rather than discarding any.

Duplicate keys are reported

a: 1 followed by a: 2 is a TypeError, not silently accepted. This is worth knowing because a duplicate key silently overrides the earlier value at runtime — a real defect that is easy to miss in review.

# Multi-document files

all-documents defaults to true, which is a deliberate improvement on 0.x. The 0.x check used a single yaml.Unmarshal, which stops after the first document — so a file whose later documents were malformed passed silently. That matters for Kubernetes manifests and any ----separated config.

Set all-documents: false for bug-for-bug 0.x parity.

Reported line numbers can be one line early

Messages come verbatim from gopkg.in/yaml.v3, which for an unterminated construct reports the line where that construct opened, not where parsing gave up. An unclosed [ on line 4 is reported as line 3. This is the library's own behaviour — identical between Unmarshal and the decoder — and is passed through unaltered rather than second-guessed. The 0.x check reported the same positions.

# Errors

Condition Behaviour
A file does not parse Not an error — reported as data, which is the point of the plugin
Input format is not map-bytes Rejected at input validation, before collection
Input path does not exist A file:lookup collection error (fatal) — there is no ignore-missing equivalent
pattern matches zero files Passes. file:lookup emits an empty map-bytes, so yaml:lint emits an empty map-string, so not:empty finds nothing to breach on

A typo'd pattern reports green, not "nothing was checked"

This is a regression from 0.x, which explicitly breached when pattern matched no files (no matching yaml files found). In 1.x, an existing path with a pattern that matches nothing is indistinguishable from a directory that is genuinely all valid YAML — both produce a passing not:empty result. A typo in pattern, or a directory that has been restructured so the pattern no longer matches anything, silently audits zero files rather than surfacing that as a problem.

Only the zero-match case is affected. A path that does not exist at all is still a file:lookup collection error and remains fatal (see the row above).

If validating that something was checked matters for your use case, assert on the underlying file:lookup fact directly. There is no generic "list must be non-empty" analyser, but if the expected filenames are known in advance, allowed:list with a required: list against file:lookup's list-string output (file-names-only left at its default true) breaches required value not found for any filename absent from the match — including when nothing matched at all.

# Differences from 0.x yamllint

Aspect 0.x 1.x
Multi-document files First document only All documents by default
Per-file passes Adds <file> has valid yaml. per valid file Only failures are reported; a clean run is a single pass on the analyser
File selection path/file/files/pattern, plus ignore-missing Delegated to file:lookup; pattern is required and a missing path is fatal
Zero files matched Breaches — no matching yaml files found Passes — see the warning above

# Data classification

Breach output carries file paths and parser messages, and a parser message can quote document content — a duplicate key's name, for example. If the linted files hold sensitive configuration, any output: sink inherits that classification. Treat accordingly where results are written to a lower classification store than the audited files.