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 != null

View on GitHub (pinned to fcec517be3)

Solutions

  1. Change the mapping target so the storage name starts with a letter and is not a keyword or reserved model/pydantic name
  2. Rename the source property in the spec so its sanitized storage name is clean
  3. 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

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


AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22). Data as JSON: /api/errors/b369af47f294584f. Report an issue: GitHub.