pytest-dev/pytest · error · SyntaxError

unexpected character/s

Error message

unexpected character/s "{value_token.value}"

What it means

A SyntaxError raised in single_kwarg when the kwarg value is neither a quoted string, a non-negative/positive integer literal, nor one of the builtin matchers (True/False/None). After failing all branches, the raw value token text is reported as unexpected. This is the value-side counterpart of errors 125/126.

Solutions

  1. Use a quoted string for non-numeric values: `foo(x='bar')`.
  2. Use plain integers (optionally negative) for numbers; avoid floats, hex, or scientific notation.
  3. Use True/False/None (unquoted) for booleans and None.
  4. For richer value types, parameterize the test in code instead of via the -m expression.

Example fix

# before
pytest -m "foo(x=bar)"
# after
pytest -m "foo(x='bar')"
Defensive patterns

Strategy: validation

Validate before calling

def is_legal_marker_value(v) -> bool:
    if isinstance(v, str):
        return True
    if isinstance(v, bool) or v is None:
        return True
    if isinstance(v, int):
        return True
    return False

Type guard

def is_supported_marker_value(v) -> bool:
    return isinstance(v, (str, int, bool)) or v is None

Prevention

When it happens

Trigger: Marker expressions like `-m "foo(x=1.5)"` (float), `-m "foo(x=bar)"` where `bar` is not True/False/None and not a digit, `-m "foo(x=-)"`, or `-m "foo(x=abc123)"`. Any IDENT value that is not a recognized builtin and not all-digits triggers it.

Common situations: Passing floats, hex, negative non-integers, enum-like names, or variables as kwarg values, expecting Python-level evaluation that the grammar does not perform.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/27783714a895dcad. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/mark/expression.py:239

    if keyword.iskeyword(keyword_name.value):
        raise SyntaxError(
            f"unexpected reserved python keyword `{keyword_name.value}`",
            (FILE_NAME, 1, keyword_name.pos + 1, s.input),
        )
    s.accept(TokenType.EQUAL, reject=True)

    if value_token := s.accept(TokenType.STRING):
        value: str | int | bool | None = value_token.value[1:-1]  # strip quotes
    else:
        value_token = s.accept(TokenType.IDENT, reject=True)
        if (number := value_token.value).isdigit() or (
            number.startswith("-") and number[1:].isdigit()
        ):
            value = int(number)
        elif value_token.value in BUILTIN_MATCHERS:
            value = BUILTIN_MATCHERS[value_token.value]
        else:
            raise SyntaxError(
                f'unexpected character/s "{value_token.value}"',
                (FILE_NAME, 1, value_token.pos + 1, s.input),
            )

    ret = ast.keyword(keyword_name.value, ast.Constant(value))
    return ret


def all_kwargs(s: Scanner) -> list[ast.keyword]:
    ret = [single_kwarg(s)]
    while s.accept(TokenType.COMMA):
        ret.append(single_kwarg(s))
    return ret


class ExpressionMatcher(Protocol):
    """A callable which, given an identifier and optional kwargs, should return
    whether it matches in an :class:`Expression` evaluation.

View on GitHub (pinned to 0d6fbdeffa)