Skip to content

fix(conversations): read message history stored as jsonb - #337

Merged
Harol-Reina merged 1 commit into
mainfrom
fix/message-history-reads-on-postgres
Oct 4, 2026
Merged

Harol-Reina merged 1 commit into
mainfrom
fix/message-history-reads-on-postgres

Conversation

@Harol-Reina

Copy link
Copy Markdown
Contributor

Summary

Every read of a stored message on PostgreSQL failed with a 500 (NotSupportedException at PostgresMessageStore.cs:188). messages.content is jsonb, which reorders object keys, so a MessageBlock's $type discriminator is no longer the first property when read back, and System.Text.Json requires it first by default.

The fix sets AllowOutOfOrderMetadataProperties = true on ConversationsJsonContext (the read path) and on PostgresJsonContext plus its explicit PostgresJson.Ctx options (same latent bug; nothing reads messages through it today).

Latent since message storage first shipped (1.2.0). Found in the LAB-W1 measurement (Platform 2.25.0 + Web 3.20.x against Postgres).

Tests (written first, red on main)

  • 7 store tests over real Postgres 18 (Testcontainers) in Stores/PostgresMessageStoreJsonbTests.cs: every IMessageStore read, all seven block types, a row inserted by hand in the format earlier releases stored (no migration needed), and a control that shows jsonb really moves $type. 6 of 7 failed on main with the production exception; the control passes by design.
  • 2 tests in PostgresJsonContextTests.cs, 1 unit test in MessageBlockTests.cs.

Gate (rebased on f8164ff, which includes the Sdk 2.7.0 cascade #332)

Release build 0 warnings · unit lane 4313/4313 · Storage.Postgres 344/344 · Identity.Redis 34/34 · coverage floor OK · endpoint invariants · script tests · OpenAPI export + fixture checks · Native AOT publish 0 IL warnings · codeql-branch: 0 results on added lines.

An adversarial review confirmed the cascade adds no JSON context, converter or store path that bypasses the fix, and that MessageBlock is the only polymorphic type persisted.

🤖 Generated with Claude Code

messages.content is a jsonb column, and Postgres reorders object keys,
so the MessageBlock "$type" discriminator is no longer the first
property when a message is read back. The source-generated reader then
threw NotSupportedException ("must specify a type discriminator") in
PostgresMessageStore.MessageRow.ToMessage(), and every message read on
Postgres answered 500. Latent since message storage first shipped (1.2.0).

Set AllowOutOfOrderMetadataProperties on ConversationsJsonContext. The
shared PostgresJsonContext also registers MessageEnvelope for jsonb
columns; it gets the same setting, in both its attribute and the
explicit options PostgresJson.Ctx is built with (those replace the
attribute wholesale). No other polymorphic type is serialized in
Platform.

Tests over a real Postgres 18 (Testcontainers) cover
GetConversationMessagesAsync, GetByIdAsync, FindByExternalIdAsync and
GetByConversationIdsAsync with text and image blocks, every block type,
and a row written by an earlier release. No migration is needed.
@Harol-Reina
Harol-Reina added this pull request to the merge queue Oct 4, 2026
Merged via the queue into main with commit 4dfb35d Oct 4, 2026
11 checks passed
@Harol-Reina
Harol-Reina deleted the fix/message-history-reads-on-postgres branch October 4, 2026 12:26
@Harol-Reina Harol-Reina added the no-spec Ships without an OpenSpec change — the only exemption (verbara-meta/ADR-0027 §6) label Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-spec Ships without an OpenSpec change — the only exemption (verbara-meta/ADR-0027 §6)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant