ErrLookup › Background articles › Python NotImplementedError: when a method, backend, or platform is deliberately unsupported
Python NotImplementedError: when a method, backend, or platform is deliberately unsupported
NotImplementedError is the signal Python libraries raise when a method exists on a type but does no real work in the current context. Developers meet it when they subclass a base class without overriding an abstract method, ask a database or result backend for a feature it structurally lacks, run on a platform missing a stdlib dependency, or push an unsupported node through a restricted query grammar. It is almost never a bug to patch around; it is a contract or capability boundary, and the fix is to choose a different method, backend, subclass, or platform rather than to force the call through.
Distilled from 198 documented records across 19 repositories.
Background
NotImplementedError is a built-in Python exception that libraries raise to mark a method that is present on a type's interface but performs no real work in this context. Unlike AttributeError, which means the attribute does not exist, NotImplementedError means the author knew about the call and deliberately declined it here. In the libraries studied, it is the standard mechanism for four related but distinct boundaries: abstract base classes enforcing an override contract, backends declining features they cannot support, restricted grammars rejecting operations outside their scope, and intentional stubs reserving a name.,The most common shape is abstract base class contract enforcement. The base method is a stub whose only job is to fail loudly if a concrete subclass forgets to override it. Django's SessionBase declares exists(), save(), and create() this way and every built-in session backend overrides all three; redis-py's CredentialProvider leaves get_credentials() unimplemented to force subclasses to supply credentials; Celery's base Backend stubs _forget() and add_to_chord(); Django's FacetsMixin and BaseFinder do the same for get_facet_counts() and check(). In each case the base class carries the signature so the type system stays consistent, and NotImplementedError converts a silent missing override into an explicit, named failure the first time the method is touched.,The second shape is capability signaling, where a working class declines an operation because its backend, transport, or platform cannot honor it. Django's spatial layer raises NotImplementedError for geodetic area on MySQL, for distance and aggregates on backends that do not override the base operations stubs; Celery's RPC backend refuses chords because its per-client reply queues have no shared join counter; urllib3 v2 rejects proxy and CONNECT-tunnel configuration on its HTTP/2 connection because the h2 state machine is bound to a single direct TLS connection. Some of these are internal signals that never reach end users, Celery's stats() wrapper already catches the rusage NotImplementedError and returns 'N/A', while others escape to whoever made the call.,A third group covers restricted grammars, out-of-context operations, and mutually exclusive configuration. pandas's eval/query engine blocks AST nodes it does not support, Lambda, IfExp, GeneratorExp, Set, is/isNot, with a per-node NotImplementedError; its HDFStore where filter refuses arithmetic, non-invert unary operators, and joint filter combinations; SQLAlchemy refuses to delete individual mapped attributes or run executemany on a streaming server-side cursor; Alembic refuses create_table and drop_table inside a batch_alter_table block. From the caller's side the meaning across all these shapes is uniform: the library understood the request and tells you it is out of scope, and the remedy is to re-route, switch backend or subclass, precompute, or update the library, not to force the method through. One residual case behaves differently: boto3 raises NotImplementedError for unsupported identifier source types and untraversable response shape types when its bundled resource model JSON disagrees with the code, a symptom that boto3 and botocore were re-paired to incompatible versions by a partial install or an over-tight pin. Reinstalling the pair together resolves it without any change to calling code.
Common causes
- Abstract base class method not overridden by a subclass.A custom backend or mixin inherits from a base class and forgets to implement a method the base deliberately leaves as a stub. Django's SessionBase (exists, save, create), FacetsMixin (get_facet_counts), and BaseFinder (check); redis-py's CredentialProvider (get_credentials); Celery's Backend (_forget, add_to_chord) all fail this way the first time the missing method is called. It is the single most frequent trigger in the family.
- Backend or feature that structurally cannot support the operation.A working class declines a request because its store, engine, or protocol has no way to honor it. Django's spatial operations raise it for geodetic area on MySQL and for distance and aggregate functions on backends that do not override the base stubs; Celery's RPC backend refuses chords because reply queues have no shared join counter; redis-py's CacheProxyConnection refuses maintenance-state APIs when the inner connection is not a MaintNotificationsAbstractConnection.
- Unsupported node or operator inside a restricted query grammar.A parser or visitor rejects constructs the engine was not built to translate. pandas's eval/query visitor raises NotImplementedError for Lambda, IfExp, GeneratorExp, Set, and is/isNot; the HDFStore where-clause evaluator refuses arithmetic operators, non-invert unary operators, and joint filter combinations that cannot be collapsed into a single filter.
- Operation invoked outside the context where it is allowed.A method is valid in general but not in the current scope. SQLAlchemy refuses to delete individual mapped attributes or run executemany on a streaming server-side cursor; Alembic refuses create_table and drop_table inside a batch_alter_table block; pandas ExtensionArray refuses view with an arbitrary dtype because reinterpretation is storage-layout-specific.
- Platform or runtime dependency unavailable.A Unix-only stdlib module or platform-specific capability is missing. Celery's WorkController.rusage raises NotImplementedError on Windows or minimal containers because the resource module could not be imported at startup; the higher-level stats() wrapper already absorbs this, so it only escapes code that calls rusage directly.
- Intentional permanent stub reserving a name.A method exists only to document that an older or planned shape is unsupported. redis-py's latency_histogram() unconditionally raises because the per-command histogram shape is intentionally not implemented and the real data lives under INFO LATENCYSTATS; SQLAlchemy's DescriptorProperty refuses column-strategy requests because it has no underlying columns.
- Incompatible bundled data after a partial install or tight version pin.Library code disagrees with its bundled model definitions. boto3 raises NotImplementedError for unsupported identifier source types and untraversable response shape types when botocore and boto3 are re-paired to versions whose resource models do not agree, a symptom of a partial reinstall or an over-tight pin rather than user code.
- Mutually exclusive transport configuration.Two transport features that cannot coexist are combined. urllib3 v2 rejects proxy and CONNECT-tunnel configuration on HTTP/2 connections because the h2 state machine is bound to a single direct TLS connection; the fix is to disable HTTP/2 or drop the proxy, not to force the combination.
What usually fixes it
- Override the contract method on your subclass, or, better, subclass a concrete built-in implementation (a Django db/cached_db/cache/file SessionStore, redis-py's UsernamePasswordCredentialProvider, Celery's KeyValueStoreBackend) that already satisfies the contract. For mixins, either implement the required method or stop inheriting the mixin.
- Switch to a backend, transport, or platform that supports the capability: PostGIS or Oracle for Django geodetic area and distance; Redis, Database, or Memcached as a Celery result backend for chords and forget; a direct connection instead of an HTTP proxy for urllib3 HTTP/2; a Unix-like platform where the resource module is available for Celery rusage.
- Move the unsupported logic out of the restricted context. Keep arithmetic, ternary, lambda, and is/isNot out of pandas eval and HDFStore where clauses and compute them in Python; keep create_table and drop_table outside Alembic batch_alter_table blocks; keep bulk executemany off connections that carry stream_results=True.
- Reinstall or update the library as a matched pair when the cause is bundled-data mismatch. Reinstall boto3 with --force-reinstall so boto3 and botocore re-pair, drop resource api_version pins that disagree with the default models, and pin both libraries together in requirements going forward.
- Fall back to a portable alternative for the unsupported computation: psutil.Process().memory_info().rss where rusage is unavailable, pyproj Geod.geometry_area_perimeter where the database cannot compute geodetic area, and plain in-memory pandas boolean indexing where an HDFStore where clause is too restricted.
Documented occurrences
- rusage not supported by this platform(celery/celery)
- Area on geodetic coordinate systems not supported.(django/django)
- Distance operations not available on this spatial backend.(django/django)
- subclasses of SessionBase must provide an exists() method(django/django)
- Can't un-map individual mapped attributes on a mapped class.(sqlalchemy/sqlalchemy)
- '{node_name}' nodes are not implemented(pandas-dev/pandas)
- Default 'empty' implementation is invalid for dtype='{dtype}'(pandas-dev/pandas)
- subclasses of SessionBase must provide a save() method(django/django)
- arithmetic operations are not supported inside an HDFStore 'where' filter; instead store a precomputed column as a data_column and query that, or read the data and apply the filter in pandas (e.g. df[df['A'] % 3 == 0]).(pandas-dev/pandas)
- get_credentials must be implemented(redis/redis-py)
- subclasses of FacetsMixin must provide a get_facet_counts() method.(django/django)
- Converting strings to {pa_type} is not implemented.(pandas-dev/pandas)
- This MapperProperty does not implement column loader strategies(sqlalchemy/sqlalchemy)
- Aggregate support not implemented for this spatial backend.(django/django)
- subclasses may provide a check() method to verify the finder is configured correctly.(django/django)
- Unsupported source type: {source}(boto/boto3)
- {dtype}(pandas-dev/pandas)
- Search path hits shape type {shape.type_name} from {item}(boto/boto3)
- UnaryOp only support invert type ops(pandas-dev/pandas)
- Proxies aren't supported with HTTP/2(urllib3/urllib3)
…and 178 more across the corpus — use search.
Honest provenance: generated on 2026-08-12 from AI-assisted analysis of the linked records. See how records are made.