Skip to content

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

  1. Start a recording session that produces at least one audio/video window.
  2. End the session (not all windows need to have been trigger-claimed by audio/frame handlers).
  3. session_end Lambda raises TypeError: Object of type Decimal is not JSON serializable.
  4. 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 TypeError on session 56c4f0aa)
  • [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