Skip to content
GitHub

Troubleshooting


ImportError: cannot import name ‘X’ from ‘lexigram.contracts’

Section titled “ImportError: cannot import name ‘X’ from ‘lexigram.contracts’”

Error:

ImportError: cannot import name 'MyType' from 'lexigram.contracts'

Cause: The name doesn’t exist in lexigram.contracts.__init__ or the lazy imports dict, or it’s been moved to a different module.

Solution:

  1. Check lexigram/contracts/__init__.py for the correct import path
  2. Search the contracts source tree for the definition
  3. If it’s a type from an extension package, import from that package instead
# ✅ Check the contracts __init__.py for lazy imports
from lexigram.contracts.core import ContainerRegistrarProtocol
from lexigram.contracts.infra.cache import CacheBackendProtocol

Error:

ImportError: cannot import name 'X' from partially initialized module 'lexigram.contracts'

Cause: A type in contracts is trying to import from an extension package, violating the zero-dependency rule.

Solution: Ensure lexigram-contracts only imports from stdlib and its own submodules — never from lexigram or any extension:

# ✅ Correct — stdlib only
from typing import Protocol
from dataclasses import dataclass
# ❌ Wrong — pulls in lexigram
from lexigram.result import Result

The Result type in contracts is defined in lexigram.contracts.core.result — independent of the lexigram package’s re-export.


isinstance(my_cache, CacheBackendProtocol) # Returns False unexpectedly

Cause: The protocol class is missing the @runtime_checkable decorator, or the implementation doesn’t structurally match the protocol signature.

Solution:

  1. Ensure the protocol has @runtime_checkable:
    from typing import Protocol, runtime_checkable
    @runtime_checkable
    class CacheBackendProtocol(Protocol):
    ...
  2. Verify your implementation has all the required methods with matching signatures
  3. Use ProtocolValidator (in lexigram) at registration time to catch mismatches

Symptom: Two different import paths for what should be the same type, leading to isinstance() failures and type errors.

Cause: A type was defined both in lexigram-contracts and in an extension package (violating the no-duplication rule).

Solution: Remove the extension package’s copy and import from contracts:

# ❌ Wrong — duplicate definition in extension package
class CacheBackendProtocol(Protocol):
...
# ✅ Correct — import from contracts
from lexigram.contracts.infra.cache import CacheBackendProtocol

Symptom: Protocol or type is hard to find, or import paths don’t match the domain organization.

Cause: A type was placed in a directory named after the extension package (e.g. ai-llm/) instead of the domain (e.g. ai/llm.py).

Solution: Organize by domain, not by consumer:

# ✅ Correct — by domain
lexigram/contracts/ai/llm.py
# ❌ Wrong — by package
lexigram/contracts/ai-llm/

Extension Exception Doesn’t Extend Contracts Base

Section titled “Extension Exception Doesn’t Extend Contracts Base”

Error:

# In extension package
class LLMRateLimitError(Exception): # ❌ Should extend AIError or LLMError
...

Cause: Leaf exceptions in extension packages must extend the corresponding base exception from contracts.

Solution:

# ✅ Correct
from lexigram.contracts.ai.exceptions import LLMError
class LLMRateLimitError(LLMError):
"""Raised when rate-limited by the LLM provider."""

This ensures callers can catch AIError and get all AI subsystem errors without importing extension packages.


  1. List all available exports:

    import lexigram.contracts
    print(dir(lexigram.contracts))
  2. Find where a protocol is defined:

    from lexigram.contracts.core.di import ContainerRegistrarProtocol
    print(ContainerRegistrarProtocol.__module__)
  3. Verify no cross-imports:

    Terminal window
    grep -rn "from lexigram\." lexigram-contracts/src/ --include="*.py" | grep -v "contracts" | grep -v "^Binary"

    Should return no results (contracts only imports from itself).

  4. Check for duplicate definitions:

    Terminal window
    grep -rn "class CacheBackendProtocol" framework/ --include="*.py"

    Should only appear in lexigram-contracts.