OpenAPITools/openapi-generator · error · IllegalArgumentException
property %s in model %s has invalid generated Python field n
Error message
property %s in model %s has invalid generated Python field name %s
What it means
Separately from the public name, the python generator validates the generated storage/field name (the attribute name on the pydantic model) whenever an explicit public name is in play. The storage name must match [A-Za-z][A-Za-z0-9_]* (must start with a letter), not be a Python keyword, and not be one of the model field name collisions, model class body names, or pydantic private member names the generator reserves.
Source
Thrown at modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java:845
throw new IllegalArgumentException(String.format(Locale.ROOT,
"property %s in model %s uses generated Python member name %s",
property.baseName, model.name, generatedMemberName));
}
if (explicitPublicName
&& (!publicName.matches("[A-Za-z_][A-Za-z0-9_]*")
|| PYTHON_KEYWORDS.contains(publicName)
|| publicName.startsWith("__"))) {
throw new IllegalArgumentException(String.format(Locale.ROOT,
"property %s in model %s cannot use %s as its public Python name",
property.baseName, model.name, publicName));
}
if (explicitPublicName
&& (!property.name.matches("[A-Za-z][A-Za-z0-9_]*")
|| PYTHON_KEYWORDS.contains(property.name)
|| MODEL_FIELD_NAME_COLLISIONS.contains(property.name)
|| MODEL_CLASS_BODY_NAMES.contains(property.name)
|| PYDANTIC_PRIVATE_MEMBER_NAMES.contains(property.name))) {
throw new IllegalArgumentException(String.format(Locale.ROOT,
"property %s in model %s has invalid generated Python field name %s",
property.baseName, model.name, property.name));
}
for (String inputName : List.of(property.baseName, publicName)) {
CodegenProperty owner = inputNameOwners.putIfAbsent(inputName, property);
if (owner != null
&& !owner.baseName.equals(property.baseName)
&& (explicitPublicName || owner.vendorExtensions.containsKey(
CodegenConstants.X_PY_EXPLICIT_PUBLIC_NAME))) {
throw new IllegalArgumentException(String.format(Locale.ROOT,
"properties %s and %s in model %s both accept input name %s",
owner.baseName, property.baseName, model.name, inputName));
}
}
for (String memberName : List.of(property.name, publicName)) {
CodegenProperty owner = memberNameOwners.putIfAbsent(memberName, property);
if (owner != nullView on GitHub (pinned to fcec517be3)
Solutions
- Change the mapping target so the storage name starts with a letter and is not a keyword or reserved model/pydantic name
- Rename the source property in the spec so its sanitized storage name is clean
- Check the reserved lists in PythonClientCodegen (MODEL_FIELD_NAME_COLLISIONS, MODEL_CLASS_BODY_NAMES, PYDANTIC_PRIVATE_MEMBER_NAMES) and avoid those exact names
Example fix
# before openapi-generator-cli generate -i api.yaml -g python --name-mapping payload=_internal,model_fields=mf # after openapi-generator-cli generate -i api.yaml -g python --name-mapping payload=internal,model_info=mf
Defensive patterns
Strategy: validation
Validate before calling
# Validate a mapping-derived storage name (Python):
import keyword
STORAGE_RE = re.compile(r'^[A-Za-z][A-Za-z0-9_]*$') # note: must start with a letter
RESERVED = {'schema', 'fields', 'model_fields', 'model_dump', 'model_computed_fields'} # extend from PythonClientCodegen
def valid_storage(name: str) -> bool:
return bool(STORAGE_RE.match(name)) and not keyword.iskeyword(name) and name not in RESERVED
assert all(valid_storage(t) for t in mapping_targets) Try / catch
try {
new DefaultGenerator().opts(input).generate();
} catch (IllegalArgumentException e) {
// message: 'property X in model Y has invalid generated Python field name Z'
// pick a storage name starting with a letter, not a keyword, not a pydantic/model reserved name
} Prevention
- Do not use underscore-prefixed mapping targets; storage names must start with a letter
- Treat pydantic v2 internals (model_*) as off-limits for storage names
- Re-run the mapping lint whenever you upgrade the generator, since reserved-name lists grow
When it happens
Trigger: -g python --name-mapping foo=_bar (mapped storage name starts with underscore, failing the stricter [A-Za-z]... regex), or a mapping that makes the storage name one of the reserved class-body/pydantic names such as schema, model_fields, or fields.
Common situations: Using an underscore-prefixed mapping target to 'hide' a field; mapping onto pydantic internals like model_dump or schema; migrating mappings between generator versions as the reserved-name lists grew.
Related errors
- property %s in model %s cannot use %s as its public Python n
- property %s in model %s uses generated Python member name %s
- properties %s and %s in model %s both accept input name %s
- properties %s and %s in model %s both use Python member name
- property %s in model %s uses generated storage name %s, whic
AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22).
Data as JSON: /api/errors/b369af47f294584f.
Report an issue: GitHub.