pydantic: Extra inputs are not permitted (tested)

Extra inputs are not permitted [type=extra_forbidden] means the model has extra="forbid" and the input contains a key that is not a field. With aliases, passing the Python field name instead of the alias also triggers it.

1. extra="ignore", "forbid", "allow"

from pydantic import BaseModel, ConfigDict, ValidationError
class Default(BaseModel):
    name: str
class Strict(BaseModel):
    model_config = ConfigDict(extra="forbid")
    name: str
class Keep(BaseModel):
    model_config = ConfigDict(extra="allow")
    name: str
data = {"name": "pen", "colour": "red"}
print("default:", repr(Default(**data)), "-> extra silently ignored")
try:
    Strict(**data)
except ValidationError as e:
    print(e)
k = Keep(**data)
print("allow:", repr(k), "| model_extra:", k.model_extra)

Output:

default: Default(name='pen') -> extra silently ignored
1 validation error for Strict
colour
  Extra inputs are not permitted [type=extra_forbidden, input_value='red', input_type=str]
    For further information visit https://errors.pydantic.dev/2.14/v/extra_forbidden
allow: Keep(name='pen', colour='red') | model_extra: {'colour': 'red'}

By default Pydantic 2 ignores unknown keys. The error only appears when the model (or a parent class, or a shared base model) sets extra="forbid"; the line above the message is the unexpected key. Either remove that key from the input, add it as a field, or switch the model to "ignore" (drop it) or "allow" (keep it in model_extra).

2. The hidden cause: aliases

from pydantic import BaseModel, ConfigDict, Field, ValidationError
class User(BaseModel):
    model_config = ConfigDict(extra="forbid")
    user_name: str = Field(alias="userName")
for data in [{"userName": "ada"}, {"user_name": "ada"}]:
    try:
        print(data, "->", repr(User(**data)))
    except ValidationError as e:
        print(data, "->", [(x["type"], x["loc"]) for x in e.errors()])
class User2(BaseModel):
    model_config = ConfigDict(extra="forbid", populate_by_name=True)
    user_name: str = Field(alias="userName")
print("populate_by_name:", repr(User2(user_name="ada")))

Output:

{'userName': 'ada'} -> User(user_name='ada')
{'user_name': 'ada'} -> [('missing', ('userName',)), ('extra_forbidden', ('user_name',))]
populate_by_name: User2(user_name='ada')

With an alias, the input must use the alias (userName). Passing the Python field name (user_name) gives two errors at once: the alias is missing and the field name is an extra input. Send the alias, or set populate_by_name=True so both work.

Checklist

Related: FastAPI 422 Unprocessable Entity ยท BaseSettings moved to pydantic-settings.

Frequently asked questions

Why does Pydantic say Extra inputs are not permitted?

The model sets extra='forbid' and the input has a key that is not a field. The key is printed on the line above the message.

Does Pydantic 2 forbid extra fields by default?

No. The default, extra='ignore', silently drops them. Only extra='forbid' raises.

Why do I get extra_forbidden for a field that exists?

It has an alias: input must use the alias. Passing the field name gives missing + extra_forbidden unless populate_by_name=True.

Written with AI assistance. Measured on 2026-10-23 with Pydantic 2.14.1 and Python 3.14.7 (python:3-alpine in Docker). Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python and Pydantic are free.