How to Fix "SyntaxError: Non-Default Argument Follows Default Argument"
Quick answer
Defining a function fails before it can even run, with Python's parser rejecting the parameter order outright. Unlike most runtime errors, this one is caught...
Defining a function fails before it can even run, with Python's parser rejecting the parameter order outright. Unlike most runtime errors, this one is caught immediately during parsing, because Python requires every parameter with a default value to come after every parameter without one β no exceptions.
The Problem
A function definition that looks reasonable fails immediately when the file is loaded:
def create_user(name="Anonymous", email):
return {"name": name, "email": email}
File "app.py", line 1
def create_user(name="Anonymous", email):
^
SyntaxError: non-default argument follows default argument
This also shows up in more complex signatures where the offending parameter isn't right next to the default one, making it slightly less obvious at a glance:
def send_email(to, subject="No Subject", body, cc=None):
...
SyntaxError: non-default argument follows default argument
Why It Happens
Python evaluates positional function arguments left to right, and once a parameter has a default value, every parameter after it must also have one β otherwise Python can't unambiguously determine which arguments were supplied positionally and which were meant to fall back to their defaults. This is a fundamental rule of the language's calling convention, enforced at parse time rather than at call time, which is why it fails before the function ever runs rather than only when called incorrectly. It commonly happens when:
- A required parameter was added to an existing function signature after a parameter that already had a default value, without reordering.
- A function was refactored and a new parameter was inserted in the middle of the parameter list rather than at the end, disrupting the required-before-optional ordering.
- Someone reasonably (but incorrectly) assumed default value placement should match logical or alphabetical order rather than Python's required syntactic order.
The Fix
Reorder parameters so every one without a default comes before every one with a default:
def create_user(email, name="Anonymous"):
return {"name": name, "email": email}
For the multi-parameter example, apply the same rule β required parameters first, defaults last:
def send_email(to, body, subject="No Subject", cc=None):
...
If the logical grouping you want genuinely doesn't fit this left-to-right ordering constraint (for example, you want callers to be able to specify cc without needing to also specify subject), make the parameters keyword-only using a bare * separator. This removes the strict ordering constraint entirely for anything after it, since keyword-only arguments must always be passed by name:
def send_email(to, body, *, subject="No Subject", cc=None):
...
With this signature, callers must pass subject and cc by keyword, but can supply them in any order and can omit either independently, without violating any positional ordering rule:
send_email("user@example.com", "Hello there", cc="manager@example.com")
If you're refactoring a widely-used function and reordering positional parameters risks breaking existing callers who rely on positional argument order, consider making all parameters keyword-only going forward, or use a Python typing overload/deprecation strategy to transition callers gradually rather than changing the signature in a single breaking step.
Still Not Working?
If you have a large, complex function signature and the error message doesn't make it immediately obvious which specific parameter is out of order, read the parameter list left to right and mark each one as either "has a default" or "no default" β the first "no default" that appears after any "has a default" is the culprit that needs to move:
def process(a, b="x", c, d="y", e):
# ^no ^yes ^no <- this one breaks the rule (comes after b's default)
...
Reorder methodically rather than guessing, moving every required parameter to the front of the list and every defaulted one to the back, or switch to keyword-only arguments after a * if the desired call-site flexibility doesn't naturally fit a strict required-then-optional ordering.
It's also worth knowing that this same ordering rule applies identically to *args and **kwargs when they're mixed into a signature with default values, since the underlying constraint is the same: Python needs to unambiguously map positional arguments at the call site to parameters in the definition, left to right, and a required parameter appearing after an optional one breaks that mapping regardless of what other syntax surrounds it:
# Valid: required, then defaulted, then *args, then keyword-only with defaults
def request(url, method="GET", *args, timeout=30, headers=None):
...
# Invalid: required parameter after *args mixed incorrectly with an early default
def request(url, method="GET", *args, path): # path required but positioned confusingly
...
Note that a parameter appearing after *args is always keyword-only regardless of whether it has a default, which is a related but distinct rule from the one covered above β worth being aware of if you're designing a signature that mixes variadic positional arguments with both required and optional named ones, since the presence of *args changes how the remaining parameters are actually callable.