netdata/netdata · error · ScannerError
expected a comment or a line break, but found %r
Error message
expected a comment or a line break, but found %r
What it means
Raised by scan_directive_ignored_line: after a directive's value, only spaces, an optional '#' comment, and a line break may follow. Any other character — e.g. a stray token after '%YAML 1.1' or after a %TAG prefix — triggers this while scanning the directive. It is the generic 'garbage at end of directive line' guard.
Source
Thrown at src/collectors/python.d.plugin/python_modules/pyyaml3/scanner.py:896
def scan_tag_directive_prefix(self, start_mark):
# See the specification for details.
value = self.scan_tag_uri('directive', start_mark)
ch = self.peek()
if ch not in '\0 \r\n\x85\u2028\u2029':
raise ScannerError("while scanning a directive", start_mark,
"expected ' ', but found %r" % ch, self.get_mark())
return value
def scan_directive_ignored_line(self, start_mark):
# See the specification for details.
while self.peek() == ' ':
self.forward()
if self.peek() == '#':
while self.peek() not in '\0\r\n\x85\u2028\u2029':
self.forward()
ch = self.peek()
if ch not in '\0\r\n\x85\u2028\u2029':
raise ScannerError("while scanning a directive", start_mark,
"expected a comment or a line break, but found %r"
% ch, self.get_mark())
self.scan_line_break()
def scan_anchor(self, TokenClass):
# The specification does not restrict characters for anchors and
# aliases. This may lead to problems, for instance, the document:
# [ *alias, value ]
# can be interpteted in two ways, as
# [ "value" ]
# and
# [ *alias , "value" ]
# Therefore we restrict aliases to numbers and ASCII letters.
start_mark = self.get_mark()
indicator = self.peek()
if indicator == '*':
name = 'alias'
else:View on GitHub (pinned to 4864de85e2)
Solutions
- Delete everything after the directive value or convert it to a '#' comment on that line.
- Confirm the directive line ends with a newline and nothing else.
- Re-parse the document to verify the scanner passes the header.
Example fix
# before %YAML 1.1 default encoding --- a: 1 # after %YAML 1.1 # default encoding --- a: 1
Defensive patterns
Strategy: validation
Validate before calling
import re
def directive_lines_clean(text):
for l in text.splitlines():
if l.startswith('%YAML'):
body = re.sub(r'\s*#.*$', '', l).rstrip()
if re.fullmatch(r'%YAML\s+\d+\.\d+', body) is None:
return False
elif l.startswith('%TAG'):
body = re.sub(r'\s*#.*$', '', l).rstrip()
if re.fullmatch(r'%TAG\s+\S+\s+\S+', body) is None:
return False
return True Type guard
def is_ignored_line_error(exc):
return isinstance(exc, yaml.scanner.ScannerError) and 'comment or a line break' in str(exc) Try / catch
try:
yaml.safe_load(text)
except yaml.scanner.ScannerError as e:
report('directive line has trailing garbage; end it after the value') Prevention
- Treat directive lines as machine-written only.
- Comments must use '#'.
- Add a pre-load lint of every '%' line.
When it happens
Trigger: yaml.safe_load() on a file where a '%' directive line ends with non-comment, non-break content such as '%YAML 1.1 foo' or '%TAG ! !x 1'.
Common situations: Notes typed after directives assuming free text is allowed; sed/patch operations appending text to header lines without a newline.
Related errors
- expected alphabetic or numeric character, but found %r
- mapping keys are not allowed here
- mapping values are not allowed here
- expected a digit or '.', but found %r
- expected a digit or ' ', but found %r
AI-assisted analysis of netdata/netdata@4864de85e2 (2026-08-15).
Data as JSON: /api/errors/b3bbda0cce3eb245.
Report an issue: GitHub.