pytest-dev/pytest · error · SyntaxError

not a valid python identifier {keyword_name.value}

Error message

not a valid python identifier {keyword_name.value}

What it means

Inside `ident(name=value)` kwargs, the `name` part must be a valid Python identifier. After lexing it as an IDENT token the parser checks `str.isidentifier()`; if it fails (e.g. starts with a digit, contains `-`), this SyntaxError is raised at the name's column.

Source

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

    if ident:
        name = ast.Name(IDENT_PREFIX + ident.value, ast.Load())
        if s.accept(TokenType.LPAREN):
            ret = ast.Call(func=name, args=[], keywords=all_kwargs(s))
            s.accept(TokenType.RPAREN, reject=True)
        else:
            ret = name
        return ret

    s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT))


BUILTIN_MATCHERS = {"True": True, "False": False, "None": None}


def single_kwarg(s: Scanner) -> ast.keyword:
    keyword_name = s.accept(TokenType.IDENT, reject=True)
    if not keyword_name.value.isidentifier():
        raise SyntaxError(
            f"not a valid python identifier {keyword_name.value}",
            (FILE_NAME, 1, keyword_name.pos + 1, s.input),
        )
    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)

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Rename the kwarg to a valid Python identifier (letters/digits/underscore, not starting with a digit).
  2. Register the marker with a proper identifier name and map the external key inside your matcher.

Example fix

# before
pytest -m 'env(target-os=linux)'
# after
pytest -m 'env(target_os="linux")'
Defensive patterns

Strategy: validation

Validate before calling

def kwarg_name_ok(name: str) -> bool:
    return name.isidentifier()

Type guard

def is_valid_kwarg_name(name: object) -> bool:
    return isinstance(name, str) and name.isidentifier()

Prevention

When it happens

Trigger: Writing `foo(1st=...)`, `foo(my-name=...)`, or any kwarg name with non-identifier characters in a `-m` expression.

Common situations: Using kebab-case or numeric-prefixed kwarg names; pasting config keys verbatim into marker expressions.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/0fc7ef7b8eea2769.json. Report an issue: GitHub.