netdata/netdata · error · ScannerError

expected a digit or ' ', but found %r

Error message

expected a digit or ' ', but found %r

What it means

Raised at the end of scan_yaml_directive_value: after the minor version number of a %YAML directive, only end-of-line, EOF, or a comment may follow. Trailing garbage after '%YAML 1.1' (extra token, letter, or dot) fails this check while scanning the directive.

Source

Thrown at src/collectors/python.d.plugin/python_modules/pyyaml3/scanner.py:841

        if ch not in '\0 \r\n\x85\u2028\u2029':
            raise ScannerError("while scanning a directive", start_mark,
                    "expected alphabetic or numeric character, but found %r"
                    % ch, self.get_mark())
        return value

    def scan_yaml_directive_value(self, start_mark):
        # See the specification for details.
        while self.peek() == ' ':
            self.forward()
        major = self.scan_yaml_directive_number(start_mark)
        if self.peek() != '.':
            raise ScannerError("while scanning a directive", start_mark,
                    "expected a digit or '.', but found %r" % self.peek(),
                    self.get_mark())
        self.forward()
        minor = self.scan_yaml_directive_number(start_mark)
        if self.peek() not in '\0 \r\n\x85\u2028\u2029':
            raise ScannerError("while scanning a directive", start_mark,
                    "expected a digit or ' ', but found %r" % self.peek(),
                    self.get_mark())
        return (major, minor)

    def scan_yaml_directive_number(self, start_mark):
        # See the specification for details.
        ch = self.peek()
        if not ('0' <= ch <= '9'):
            raise ScannerError("while scanning a directive", start_mark,
                    "expected a digit, but found %r" % ch, self.get_mark())
        length = 0
        while '0' <= self.peek(length) <= '9':
            length += 1
        value = int(self.prefix(length))
        self.forward(length)
        return value

    def scan_tag_directive_value(self, start_mark):

View on GitHub (pinned to 4864de85e2)

Solutions

  1. Put anything after the version on its own line or make it a comment: '%YAML 1.2 # standard'.
  2. Remove trailing tokens so the directive line ends right after the minor number.
  3. Re-validate the file parses.

Example fix

# before
%YAML 1.2 standard
---
a: 1

# after
%YAML 1.2  # standard
---
a: 1
Defensive patterns

Strategy: validation

Validate before calling

import re

def no_trailing_after_version(text):
    return all(re.match(r'^%YAML\s+\d+\.\d+(\s*#.*)?\s*$', l)
               for l in text.splitlines() if l.startswith('%YAML'))

Type guard

def is_directive_tail_error(exc):
    return isinstance(exc, yaml.scanner.ScannerError) and 'digit or' in str(exc) and 'directive' in str(exc)

Try / catch

try:
    yaml.safe_load(text)
except yaml.scanner.ScannerError as e:
    report('nothing may follow the %YAML version except a # comment')

Prevention

When it happens

Trigger: yaml.safe_load() on input with '%YAML 1.2 extra', '%YAML 1.1.1', '%YAML 1.1x' — anything after the minor number that is not whitespace-then-EOL/comment.

Common situations: Comments attached without '#': '%YAML 1.2 standard'; build scripts appending flags to the header line; a duplicated version token from templating.

Related errors


AI-assisted analysis of netdata/netdata@4864de85e2 (2026-08-15). Data as JSON: /api/errors/5d993e002fc4408f. Report an issue: GitHub.