Quick answer: This SyntaxError occurs when a function call places an expression on the left side of a keyword argument. Keyword names must be valid identifiers; put expressions on the right side, use positional arguments, or build a mapping and unpack it with **.

SyntaxError: keyword can't be an expression happens when Python sees something that is not a valid keyword argument name on the left side of = in a function call.
The main references are Python’s function call reference, the keyword argument tutorial, and the dictionary tutorial.
Keyword argument names must be valid Python identifiers, such as name, limit, or sort_order. Strings, numbers, attribute access, and computed expressions cannot be written directly as keyword names.
The fix is to use a valid keyword name, use a dictionary literal, or unpack a dictionary with **.
This error is different from a runtime TypeError. A syntax error means Python cannot parse the code, so the program never reaches execution.
Recognize The Error
You can reproduce the syntax error by compiling a call where a string is used as the keyword name.
For example, code shaped like save("first-name"="Ada") cannot parse because the keyword name is a string expression.
The left side of a keyword argument must be a name, not a string expression.
Because this is a syntax error, Python stops before the code can run.
Look at the function call shown in the traceback and inspect the text directly before the = sign.
Use A Valid Keyword Name
If the function accepts a normal parameter, pass it by that parameter name.
def save_user(first_name, active=True):
return {"first_name": first_name, "active": active}
user = save_user(first_name="Ada", active=True)
print(user)
first_name is a valid Python identifier, so it can be used as a keyword argument.
This is the simplest fix when you control the function signature.
Use snake_case names for parameters that callers will pass by keyword. That keeps the call readable and compatible with Python’s identifier rules.

Use A Dictionary Literal For String Keys
If the key needs a hyphen, space, or other non-identifier character, build a dictionary instead of a function call.
payload = {
"first-name": "Ada",
"account status": "active",
}
print(payload["first-name"])
print(payload["account status"])
Dictionary keys can be strings that are not valid keyword names.
Use this for JSON payloads, API data, and external field names.
This is also the right choice when keys come from a file, form, or response body and should remain exactly as provided.
Unpack A Dictionary Into A Call
If a function accepts keyword arguments and you already have a dictionary, unpack it with **.
def save_user(first_name, role):
return f"{first_name}: {role}"
options = {"first_name": "Grace", "role": "admin"}
result = save_user(**options)
print(result)
The dictionary keys must match parameter names accepted by the function.
If a key is unexpected, Python raises a TypeError at runtime.
That runtime error is easier to handle than invalid syntax because your program can catch it or validate the keys before calling the function.
Convert External Keys First
External data often uses keys that are not Python identifiers. Convert those keys before calling a Python function.
def save_user(first_name):
return first_name.upper()
payload = {"first-name": "Linus"}
options = {"first_name": payload["first-name"]}
print(save_user(**options))
This keeps external field names at the boundary and internal function calls clean.
It also gives you a clear place to validate required fields.
Keep conversion code close to the input boundary. The rest of your program can then use normal Python-friendly parameter names.

Avoid Expressions On The Left Side
Computed keys belong in a dictionary, not on the left side of a keyword argument.
field = "name"
value = "Ada"
payload = {field: value}
print(payload)
Use a dictionary when the key is chosen at runtime.
Use keyword arguments when the parameter name is known in the source code.
If the key changes while the program runs, a dictionary is the correct data structure.
Use kwargs For Flexible Names
A function can accept flexible keyword names with **kwargs.
def show_options(**kwargs):
for key, value in kwargs.items():
print(key, value)
show_options(name="Ada", role="admin")
Even with **kwargs, direct keyword names in the call must be valid identifiers.
For keys that are not valid identifiers, pass a dictionary with show_options(**options) after converting the keys to acceptable names, or process the dictionary directly.
The practical rule is simple: keyword argument names must look like Python names. If your key is a string, computed value, or external field name, use a dictionary and unpack only when the keys match the function signature. The keyword-name restriction makes more sense alongside normal named calls; Python Keyword Arguments Guide explains defaults, ordering, unpacking, and keyword-only parameters.
When debugging, avoid changing many calls at once. Fix the first failing call, confirm the intended function signature, then apply the same pattern to similar calls.
That keeps syntax fixes predictable and easy to review.
Then run the file again.

Keep The Name A Valid Identifier
A keyword call uses name=value, so the name cannot be a string literal, calculation, subscript, or other expression. The value on the right may be any valid expression.
Use ** For Dynamic Names
When keyword names come from data, create a dictionary and unpack it into the call. Validate allowed names first if the mapping comes from outside the program, because unknown names may still raise TypeError.
Use Positional Arguments When Appropriate
A positional argument is the correct representation when there is no fixed identifier-like parameter name or when the API is designed around positional input. Do not force dynamic values into keyword syntax.

Read The Traceback Location
The parser reports the call site, but the conceptual problem is the token before the equals sign. Check for a quoted key, arithmetic expression, subscript, or other expression in that position.
Keep The Function Signature Clear
If an API genuinely needs dynamic options, accept a mapping explicitly or use controlled **kwargs and reject unknown keys. A permissive forwarding layer should not hide misspellings or unsupported configuration.
Python’s calls reference defines positional, keyword, and mapping-unpacking syntax. Related references include keyword arguments, dynamic names, and error-path tests.
For related call syntax, compare keyword arguments, dynamic names, and error-path tests when passing options.
Frequently Asked Questions
What causes keyword cannot be an expression?
A function call uses an expression such as a string literal, calculation, or subscript on the left side of a keyword argument.
How do I pass a dynamic keyword name?
Build a dictionary and unpack it with ** when the function accepts keyword arguments.
Can a keyword argument value be an expression?
Yes. Expressions are valid on the right side, such as timeout=base + extra; the name itself must be valid syntax.
When should I use positional arguments?
Use a positional argument when the value does not have a fixed identifier name or the API is designed around positional input.