Package-source copy · Generated into the site so runtime docs are searchable and linkable.
Troubleshooting (Python)
This guide maps common problems to verified fixes for the Python SDK.
Error: NoCredentialsError when using DynamoDB Local
Cause: boto3 still requires credentials even for DynamoDB Local.
Solution: Provide dummy credentials:
client = boto3.client(
"dynamodb",
endpoint_url="http://localhost:8000",
region_name="us-east-1",
aws_access_key_id="dummy",
aws_secret_access_key="dummy",
)
Error: encrypted fields without kms_key_arn
Cause: models with encrypted fields fail closed unless kms_key_arn is configured.
Solution: pass kms_key_arn and (in tests) inject a fake KMS client.
Confusion: Git tag vX.Y.Z-rc.N vs Python version X.Y.ZrcN
Cause: Python follows PEP 440 prerelease normalization.
Solution: use the wheel filename from the GitHub Release asset list (e.g., tabletheory_py-1.2.1rc1-...whl).
Error: ResourceNotFoundException or TableNotFoundError
Cause: the ModelDefinition table name does not exist at the configured boto3 endpoint/region, or the client points
at AWS when you expected DynamoDB Local.
Fix: verify table_name, endpoint_url, and region_name, then create the table through infrastructure or your
integration-test setup.
client = boto3.client(
"dynamodb",
endpoint_url="http://localhost:8000",
region_name="us-east-1",
aws_access_key_id="dummy",
aws_secret_access_key="dummy",
)
Error: missing or empty key fields
Cause: put, get, update, or delete received an item/key without the dataclass fields marked roles=["pk"]
and roles=["sk"]. DynamoDB key attributes cannot be empty strings.
Fix: use a small key helper and keep field names aligned with theorydb_field(name=...).
def profile_key(user_id: str) -> tuple[str, str]:
return (f"USER#{user_id}", "PROFILE")
pk, sk = profile_key("42")
item = table.get(pk, sk)
Error: GSI query returns no items or rejects consistent reads
Cause: GSI reads require index_name=..., and DynamoDB rejects consistent_read=True for GSIs.
Fix: pass the index name and leave consistent_read at its default False.
page = table.query("EMAIL#a@example.com", index_name="gsi_email", limit=25)
Error: encrypted fields without kms_key_arn
Cause: models with encrypted fields fail closed unless kms_key_arn is configured.
Fix: pass kms_key_arn in production and inject FakeKmsClient in tests. Do not remove encrypted=True to make a
local test pass.
Error: encrypted field used in a filter or condition
Cause: DynamoDB sees encrypted envelopes, not plaintext, so encrypted attributes cannot be queried or compared.
Fix: query by plaintext PK/SK/GSI attributes and keep secrets in non-key encrypted fields.
Pagination surprise: query_all or scan_all loads too much data
Cause: query_all, scan_all, and scan_all_segments follow every cursor and materialize the full result set.
Fix: use query, query_iter, scan, or scan_iter for large reads.
for page in table.query_iter("TENANT#1", limit=100):
for item in page.items:
if done(item):
break
break
Testing surprise: timestamp assertions are flaky
Cause: lifecycle fields are populated from the runtime clock.
Fix: inject now and strict fakes in unit tests.
from datetime import datetime, timezone
from tabletheory_py.mocks import FakeDynamoDBClient
table = Table(
model,
client=FakeDynamoDBClient(),
now=lambda: datetime(2026, 1, 16, tzinfo=timezone.utc),
)