sqlalchemy/sqlalchemy · error · ArgumentError
Unexpected value for attribute '{base.__name__}.{name}'. Exp
Error message
Unexpected value for attribute '{base.__name__}.{name}'. Expected a Column, not: {type(obj)} What it means
_extract_columns_from_class iterates each non-dunder attribute of a TypedColumns subclass. An attribute is expected to be either None (annotation-only) or a Column instance. Any other value type (an int, a string, a ColumnClause, a relationship, a random object) is rejected with ArgumentError showing the actual type, because the column-extraction logic has no way to interpret it.
Source
Thrown at lib/sqlalchemy/sql/_annotated_cols.py:350
if anno_sqltype is None and not col.foreign_keys:
raise ArgumentError(
"Python typing annotation is required for "
f"attribute '{base.__name__}.{name}' when "
"primary argument(s) for Column construct are "
"None or not present"
)
elif anno_sqltype is not None:
col._set_type(anno_sqltype)
if (
nullable is not _NoArg.NO_ARG
and col._user_defined_nullable is NULL_UNSPECIFIED
and not col.primary_key
):
col.nullable = nullable
columns[name] = col
else:
raise ArgumentError(
f"Unexpected value for attribute '{base.__name__}.{name}'"
f". Expected a Column, not: {type(obj)}"
)
# Return columns as a list
return list(columns.values())
@util.preload_module("sqlalchemy.sql.schema")
def _collect_annotation(
cls: type[Any], name: str, module: str, raw_annotation: _AnnotationScanType
) -> _AnnotationScanType | Literal[_NoArg.NO_ARG]:
Column = util.preloaded.sql_schema.Column
_locals = {"Column": Column, "Named": Named}
# _ClassScanAbstractConfig._collect_annotation & _extract_mapped_subtype
try:
annotation = sa_typing.de_stringify_annotation(View on GitHub (pinned to d9b44cb731)
Solutions
- Remove the non-column attribute or move it elsewhere (constant, mixin, separate class)
- If it should be a column, use schema.Column: `from sqlalchemy import Column` and assign a Column instance
- If it is metadata for type checkers only, prefix with an underscore or place it outside the TypedColumns subclass
Example fix
# before
from sqlalchemy.sql import column # wrong column
class Cols(TypedColumns):
c = column('x') # ColumnClause, not schema.Column -> rejected
# after
from sqlalchemy import Column, String
class Cols(TypedColumns):
c = Column('x', String) Defensive patterns
Strategy: type-guard
Validate before calling
from sqlalchemy import Column
def validate_attribute(name, obj) -> None:
if obj is not None and not isinstance(obj, Column):
raise TypeError(
f"TypedColumns attribute {name!r} must be a Column, "
f"got {type(obj).__name__}"
) Type guard
from sqlalchemy import Column
def is_valid_column_attr(obj) -> bool:
"""True only for None or a Column (valid TypedColumns attribute values)."""
return obj is None or isinstance(obj, Column) Prevention
- In TypedColumns subclasses, assign only `Column(...)` objects or nothing (use an annotation).
- Don't place plain values, lambdas, or other constructs as class attributes in a TypedColumns subclass.
- Add a test asserting every non-annotation attribute is an instance of Column.
When it happens
Trigger: Assigning a non-Column value as a class attribute in a TypedColumns subclass: `count = 5`, `label = 'default'`, `related = relationship(...)`, or `c = column('x')` (a ColumnClause, not schema.Column).
Common situations: Treating TypedColumns like an ORM mapped class and adding class-level constants, defaults, or relationship() calls. Also occurs when importing the wrong Column (e.g. sqlalchemy.sql.expression.column instead of sqlalchemy.Column).
Related errors
- Python typing annotation is required for attribute '{base.__
- No type information could be extracted from annotation {anno
- Could not find a SQL type for type {extracted_type} obtained
- Could not interpret annotation {raw_annotation} for attribut
- Cannot instantiate a TypedColumns object.
AI-assisted analysis of sqlalchemy/sqlalchemy@d9b44cb731 (2026-08-01).
Data as JSON: /data/errors/3cfbc8128e19a1a8.json.
Report an issue: GitHub.