TableTheory Troubleshooting
This guide provides solutions to common issues with verified fixes, categorized by the type of error you might encounter.
TypeScript and Python ship their own runtime-specific troubleshooting guides as sibling package surfaces in the shared TheoryCloud TableTheory subtree. This page is the Go troubleshooting guide.
Quick Diagnosis
| Symptom | Likely Cause | Section |
|---|---|---|
ValidationException |
Struct tags mismatch / Type mismatch | Validation Errors |
ResourceNotFoundException |
Table missing or wrong Region | Configuration Errors |
TransactionCanceledException |
Condition check failed / Conflict | Transaction Errors |
ProvisionedThroughputExceeded |
Insufficient RCU/WCU | Capacity Errors |
| Slow Cold Starts (>100ms) | Handler initialization | Performance Issues |
| Fields missing in DynamoDB | Missing json tags |
Data Modeling Issues |
Validation Errors
“One or more parameter values were invalid” / “An attribute value must not be empty”
Cause:
You are trying to write an item where the Primary Key (PK) or Sort Key (SK) is empty string "". DynamoDB does not allow empty strings for key attributes.
Solution:
Ensure your struct fields are populated before calling Create().
// ❌ INCORRECT: ID is empty
user := &User{Name: "John"}
db.Model(user).Create() // Fails
// ✅ CORRECT
user := &User{ID: "user_123", Name: "John"}
db.Model(user).Create()
“Member must satisfy enum value set: [BEGINS_WITH, BETWEEN, …]”
Cause:
You used an invalid operator in Where().
Solution: Check your operator string.
- Valid for Key:
=,<,>,<=,>=,BEGINS_WITH,BETWEEN. - Valid for Filter: All of the above plus
IN,CONTAINS.
Configuration Errors
“ResourceNotFoundException: Requested resource not found”
Cause:
- The table does not exist in the configured AWS Region.
- TableTheory is inferring the wrong table name.
Solution:
- Check your region in
session.Config. - Check the table name. By default,
Userstruct maps touserstable. - If you use custom table names, implement the
TableName()interface.
// Optional: Custom table name
func (u User) TableName() string {
return "my_custom_user_table"
}
Transaction Errors
“TransactionCanceledException” and cancellation reasons
A DynamoDB transaction is atomic: when any operation’s condition fails, nothing
is committed and DynamoDB returns a TransactionCanceledException with one
cancellation reason per operation, in order ("None" for the operations that
did not fail). TableTheory classifies that exception for you instead of leaving
you to parse message text.
Classification (identical in Go, TypeScript and Python). The FIRST genuine
failure — the first reason whose code is neither missing nor "None" — decides
the error:
| DynamoDB reason code | Go sentinel |
|---|---|
ConditionalCheckFailed |
errors.ErrConditionFailed |
TransactionConflict |
errors.ErrTransactionConflict |
ThrottlingException, ThrottlingError, ProvisionedThroughputExceeded, RequestLimitExceeded, InternalServerError, ServiceUnavailable |
errors.ErrThrottled |
| anything else, no genuine failure, or an empty reason list | errors.ErrTransactionFailed |
An unknown or missing code is never reclassified as a condition failure, and an
ambiguous transport error (a timeout, a dial tcp failure, a connection reset)
is never turned into a transaction or condition error — it passes through
unchanged.
A cancellation with no genuine per-operation failure — an empty reason list,
or every reason missing its code or carrying the "None" placeholder — reports
errors.ErrTransactionFailed and carries OperationIndex: -1. That deliberate
marker keeps it distinct from a real failure at operation 0 (where
OperationIndex is 0); Operation and Reason are empty on that path.
Inspect the cause and every reason. Use errors.Is to match a sentinel and
errors.As to recover structure and the original AWS exception.
err := db.TransactWrite(ctx, func(tx core.TransactionBuilder) error {
tx.Put(user, tabletheory.IfNotExists())
return nil
})
switch {
case errors.Is(err, theorydbErrors.ErrConditionFailed):
// A conditional write lost; nothing was committed.
case errors.Is(err, theorydbErrors.ErrTransactionConflict):
// A racing transaction conflict.
case errors.Is(err, theorydbErrors.ErrThrottled):
// Throttled; back off.
case errors.Is(err, theorydbErrors.ErrTransactionFailed):
// Canceled with no classifiable reason.
}
var txErr *theorydbErrors.TransactionError
if errors.As(err, &txErr) {
// First genuine failure (unchanged meaning).
log.Printf("op %d (%s) failed: %s", txErr.OperationIndex, txErr.Operation, txErr.Reason)
// Every reason, in original order (positions match the operations).
for _, reason := range txErr.Reasons {
log.Printf(" [%d] %s: %s", reason.Index, reason.Code, reason.Message)
}
// Only the genuine failures, in order.
for _, failure := range txErr.RealFailures() {
log.Printf(" failure [%d] %s", failure.Index, failure.Code)
}
}
// The original SDK exception (and its ordered CancellationReasons) stays
// recoverable, even when the error is wrapped by an intermediate.
var awsCanceled *types.TransactionCanceledException
if errors.As(err, &awsCanceled) {
// awsCanceled.CancellationReasons is the raw AWS list.
}
errors.Is is the supported comparison — do not compare transaction sentinels
with ==: the transaction entry points return a structured error that unwraps
to the sentinel.
Item images are never exposed on the typed surface. A cancellation reason
carries only its code and message. TableTheory never copies the DynamoDB Item
attribute map into TransactionError, so Error(), fmt formatting and logs of
the structured error never include item data or attribute values. The original
exception is retained on the exported Cause field (and recoverable with
errors.As); when the request returned item images (for example with
ReturnValuesOnConditionCheckFailure) a reflective or serializing dump of that
raw exception — %#v, a JSON/reflection dump of the SDK error — can include
them. Log the typed fields as shown above.
Safe logging helpers and opt-in item-image redaction. Go’s Error(),
%v/%+v/%s/%q and the typed fields above carry no item data; only a
reflective dump of the raw Cause can. In TypeScript and Python the mapped error
retains the raw SDK exception on cause / __cause__, and that object is
walked by a whole-error serialization — JSON.stringify(err), console.log(err),
util.inspect, or a structured logger or error tracker that dumps the cause — so
it can include CancellationReasons[].Item or a condition failure’s Item.
Both runtimes expose a typed-only projection, and an opt-in option that strips item images from the retained cause:
| Runtime | Safe projection | Opt-in redaction |
|---|---|---|
| TypeScript | safeErrorFields(err) → { code, codes, message, cancellationReasons }, or undefined when the value is not a TheorydbError |
new TheorydbClient(ddb, { redactItemImages: true }) |
| Python | err.safe_dict() → { "type", "message", ...typed fields } |
Table(model, client=..., redact_item_images=True) |
safeErrorFields never includes cause, so it cannot reach item images.
safe_dict() returns only the typed surface and never __cause__ or any
request/response payload.
Python coverage boundary. redact_item_images covers the client a Table
uses. A component you hand a raw client to does not inherit the option — a
LeaseManager keeps the client you pass it — so wrap that client with
redacting_client from tabletheory_py.aws_errors (it is not a root export):
from tabletheory_py import LeaseManager
from tabletheory_py.aws_errors import redacting_client
leases = LeaseManager(
client=redacting_client(boto3.client("dynamodb")),
table_name="Leases",
)
The generated Python API reference does not render the Table constructor, so it
does not list redact_item_images; the
Python troubleshooting guide documents the
option and redacting_client.
TypeScript coverage boundary. redactItemImages plumbs through
TheorydbClient, so every error a client maps is redacted. lease.ts’s three
mapDynamoError call sites and update-builder’s execute() map errors directly
and cannot inherit it, and there is no standalone TypeScript wrapper. Neither
runtime sets ReturnValuesOnConditionCheckFailure, so those paths cannot carry
item images today; revisit if ALL_OLD support is ever added. See the
TypeScript troubleshooting guide.
The redaction option is off by default, so default behavior is unchanged: the
retained cause/__cause__ is the same object it always was. When it is enabled,
the cause becomes a copy whose CancellationReasons[].Item (and a condition
failure’s top-level Item) are removed, and the original error object is never
mutated. Until it is enabled, dumping a whole error with the default settings can
still reach the item images. Closing that default gap would change the retained
cause’s identity for every consumer, so it belongs to a future major release — it
is not done here.
Capacity Errors
“ProvisionedThroughputExceededException”
Cause: Your application is exceeding the RCU/WCU limits of the table.
Solution:
- Short Term: Enable auto-scaling on your DynamoDB table.
- Retry Config: Increase
MaxRetriesinsession.Config. - Code Fix: Reduce batch sizes or use
BatchGetWithOptionswith a rate limiter.
// Increase retries for bursty workloads
db, _ := tabletheory.New(session.Config{
MaxRetries: 10, // Default is 3
})
Data Modeling Issues
Fields are saving as “AttributeName” (Title Case) but I want “attribute_name” (snake_case)
Cause: TableTheory uses the struct field name by default if no tag is present.
Solution:
Add json tags to your struct. TableTheory respects json tags for attribute naming.
type User struct {
// Saves as "first_name" in DynamoDB
FirstName string `json:"first_name"`
// Saves as "LastName" (Go field name)
LastName string
}
“Item not found” when querying by GSI
Cause:
You are using Where() on a GSI key but forgot to call Index(). Without Index(), TableTheory assumes you are querying the main table’s Primary Key.
Solution: Explicitly specify the index name.
// ❌ INCORRECT: Assumes Email is the table's PK
db.Model(&User{}).Where("Email", "=", "me@example.com").First(&u)
// ✅ CORRECT: Tells DynamoDB to look at the GSI
db.Model(&User{}).Index("email-index").Where("Email", "=", "me@example.com").First(&u)
Performance Issues
Slow Lambda Cold Starts
Diagnosis: Check your logs for initialization time. If > 500ms, you are likely creating a new AWS session per invocation.
Solution:
Use the LambdaInit helper in your init() function.
var db *tabletheory.LambdaDB
func init() {
// Pre-registers models and warms up connection
db, _ = tabletheory.LambdaInit(&User{}, &Order{})
}
Production Scenarios & Incident Management
This section covers advanced troubleshooting and incident response in production environments.
Emergency Procedures
Table Accidentally Deleted
Immediate Action:
- STOP Deployments: Halt any ongoing CI/CD pipelines to prevent further damage.
- Restore from Backup: If point-in-time recovery (PITR) is enabled, restore the table to the last known good state. If not, recover from the most recent daily/weekly backup.
- Notify Team: Alert relevant stakeholders (DevOps, SRE, affected teams).
Prevention:
- Implement strong IAM policies to restrict
DeleteTablepermissions. - Enable PITR on all production tables.
- Use infrastructure-as-code (IaC) for all table management.
Wrong Region Deployment
Immediate Action:
- Verify Region: Confirm the
Regionin yoursession.Configand AWS environment variables. - Rollback: Deploy to the correct region, or roll back to a known good version.
- Clean Up: Manually delete any resources accidentally created in the wrong region.
Prevention:
- Enforce region specificity in CI/CD pipelines.
- Use environment variables (e.g.,
AWS_REGION) consistently.
Performance Debugging
Slow Queries / High Costs
Diagnosis:
- CloudWatch Metrics: Monitor
ConsumedReadCapacityUnits,ConsumedWriteCapacityUnits,ThrottledRequestsfor your DynamoDB table and indexes. - AWS X-Ray: Trace requests to identify bottlenecks in your Lambda function or DynamoDB calls.
- TableTheory Logs: Enable detailed logging within TableTheory (if available) to see query plans.
Solution:
- Eliminate Scans: Refactor code to use
Query()with proper PK/SK conditions or GSIs. - Optimize Indexes: Ensure GSIs are correctly used and projected attributes are minimal.
- Batching: Group multiple
GetItemorPutItemrequests intoBatchGetorBatchWritecalls. - Lambda Profiling: Use
LambdaDB.GetMemoryStats()and Go pprof to identify CPU/memory intensive code within your Lambda.
High Cold Start Latency
Diagnosis:
- Check CloudWatch Logs for
REPORTlines, specificallyInit Duration. - Monitor the time taken by your
init()function.
Solution:
- Ensure
tabletheory.LambdaInit()is called once ininit(). - Pre-register all models with
LambdaInit()to avoid reflection at runtime. - Increase Lambda memory. This can indirectly speed up initialization.
Monitoring & Alerting Setup
Recommendations:
- CloudWatch Alarms: Set up alarms for:
ConsumedReadCapacityUnits(above provisioned/on-demand thresholds)ConsumedWriteCapacityUnits(above provisioned/on-demand thresholds)ThrottledRequests(any non-zero count)IteratorAge(for DynamoDB Streams, indicates backlog)
- X-Ray Tracing: Enable X-Ray for all Lambda functions interacting with TableTheory to get detailed trace maps and performance breakdowns.
- Custom Metrics: Emit custom metrics for critical business operations, latency, and error rates.
Production Incident Playbooks
General Steps:
- Detect: Alerts from CloudWatch, PagerDuty, etc.
- Assess: Verify the issue, identify impacted services/customers.
- Diagnose: Use logs (CloudWatch Logs, application logs), traces (X-Ray), and metrics (CloudWatch) to pinpoint the root cause.
- Mitigate: Implement temporary fixes (e.g., disable a feature, scale up capacity).
- Resolve: Apply permanent fix (code deployment, infrastructure change).
- Post-Mortem: Document the incident, root cause, and preventative measures.
Key Metrics to Monitor:
- API Latency: End-to-end response times.
- Error Rate: Percentage of failed requests.
- Throughput: Requests per second.
- DynamoDB Consumed Capacity: Read/Write units consumed.
- Lambda Invocation Duration & Errors: Monitor Lambda function health.
Communication: Always have a clear communication plan for incidents, informing stakeholders about impact, status, and resolution.