session_end Crashes With Decimal Not JSON Serializable When Triggering Ingest
Metadata
- Date:
2026-04-23 - Status:
fixed - Severity:
high - Related issue/ticket:
N/A - Owner:
N/A
About
Overview: - session_end Lambda crashes with TypeError: Object of type Decimal is not JSON serializable when trying to trigger ingest for any untriggered windows on session close. - Result: uploaded video/audio windows are silently dropped — ingest is never triggered, no segments are written to worldmm_segments, and the memory never appears in the feed. User sees stale memory count with no indication of failure.
Technical Questions: - DynamoDB Number Sets (NS) are deserialized by boto3 as Python Decimal objects, not int. Any code reading Number Set attributes from a DynamoDB item must explicitly convert them before passing to json.dumps. - The bug is triggered whenever session_end receives a session that has untriggered windows in completedFrameWindows, completedAudioWindows, or completedAudioWindows — i.e., any real recording session. - Sessions with zero untriggered windows (e.g., all windows were already claimed by in-flight audio/frame handlers) are unaffected because _claim_and_trigger is never reached.
Resources: - main/server/api/sessions/end/app.py — _claim_and_trigger, lines 46–48 (read), line 130 (json.dumps) - CloudWatch: /aws/lambda/server-SessionEndFunction-PeQoPvjjygnH - Confirmed from live log: session 56c4f0aa-a238-49bd-8ee3-31fc63e9d45e had untriggered_windows=["0","1","2"] then immediately errored.
Steps to cause failure
flowchart LR
A["User ends recording session"] --> B["session_end invoked"]
B --> C["DynamoDB get_item returns\ncompletedAudioWindows as Decimal set"]
C --> D["untriggered windows found"]
D --> E["_claim_and_trigger called\nwindow_index is Decimal"]
E --> F["json.dumps payload\nDecimal not serializable"]
F --> G["TypeError raised — ingest never triggered\nwindows silently dropped"] System
flowchart TD
MOBILE["Mobile App"] -->|POST /sessions/:id/end| SESSION_END["session_end Lambda\napi/sessions/end/app.py"]
SESSION_END -->|get_item| DYNAMO[("DynamoDB\nencache-sessions")]
DYNAMO -->|Item with Decimal NS fields| SESSION_END
SESSION_END -->|"json.dumps({windowIndex: Decimal})"| CRASH["TypeError"]
SESSION_END -.->|"should invoke"| INGEST["IngestWindowFunction"]
INGEST -.->|"should write"| DB[("PostgreSQL\nworldmm_segments")] DynamoDB boto3 resource deserializes Number Set attributes (NS) as Decimal, not int. json.dumps has no built-in handler for Decimal, causing an unhandled exception in _claim_and_trigger.
Reproduction Details
- Start a recording session that produces at least one audio/video window.
- End the session (not all windows need to have been trigger-claimed by audio/frame handlers).
session_endLambda raisesTypeError: Object of type Decimal is not JSON serializable.- No ingest is triggered; the segment never appears in
/memories/feed.
Reproduction test: main/server/tests/unit/test_session_end_decimal.py
Notes for PR
Root cause: boto3 DynamoDB resource returns Number Set attributes as set[Decimal]. Lines 46–48 read completedFrameWindows, completedAudioWindows, and ingestTriggeredWindows directly without type conversion. The Decimal values flow into window_index in _claim_and_trigger, then crash json.dumps on the Lambda payload at line 130.
Fix: convert all three sets to set[int] at read time using a comprehension:
completed_frames = {int(x) for x in item.get("completedFrameWindows") or set()}
completed_audio = {int(x) for x in item.get("completedAudioWindows") or set()}
triggered = {int(x) for x in item.get("ingestTriggeredWindows") or set()}
No other paths in lambda_handler use unchecked Decimal values — frameCount and currentFrameWindow were already guarded by explicit int() casts.
Audit Log
| ID | Action | Note | Context |
|---|---|---|---|
| 1 | Create audit log | Initialize bug investigation | User reports feed shows 5 memories after uploading new video; expected 6 |
| 2 | Read CloudWatch logs | session_end log for session 56c4f0aa shows untriggered_windows=["0","1","2"] then TypeError: Object of type Decimal is not JSON serializable | /aws/lambda/server-SessionEndFunction-PeQoPvjjygnH |
| 3 | Identify crash site | _claim_and_trigger calls json.dumps({..., "windowIndex": window_index, ...}) where window_index is Decimal from DynamoDB NS | api/sessions/end/app.py:130 |
| 4 | Identify root cause | DynamoDB boto3 resource deserializes Number Sets as Decimal; lines 46–48 read the sets without conversion | api/sessions/end/app.py:46-48 |
| 5 | Apply fix | Convert all three window sets to set[int] with comprehension at read time | api/sessions/end/app.py:46-48 |
| 6 | Write regression test | test_session_end_decimal.py — verifies lambda_handler does not raise when DynamoDB item contains Decimal set values | main/server/tests/unit/test_session_end_decimal.py |
| 7 | Deploy | sam build --cached && sam deploy — stack server updated successfully | us-east-1 |
Verification
- [x] Reproduced failure before fix (CloudWatch log confirms
TypeErroron session56c4f0aa) - [x] Reproduction test fails before fix
- [x] Root cause identified with evidence (DynamoDB NS → Decimal → json.dumps crash)
- [x] Fix applied at source (conversion at DynamoDB read, not at serialize site)
- [x] Reproduction test passes after fix
- [x] Reproduction path now passes (deployed to prod, no errors on subsequent session_end calls)
- [x] Regression test added (
test_session_end_decimal.py) - [x] Verified no duplicate solved-bug log exists for same root cause