Skip to content

fix(#4625): reading events/snapshots stored without a revision [DRAFT - reproduction, solution under discussion]#4626

Draft
MateuszNaKodach wants to merge 1 commit into
axon-5.1.xfrom
fix/4625_MessageWithoutRevision
Draft

fix(#4625): reading events/snapshots stored without a revision [DRAFT - reproduction, solution under discussion]#4626
MateuszNaKodach wants to merge 1 commit into
axon-5.1.xfrom
fix/4625_MessageWithoutRevision

Conversation

@MateuszNaKodach
Copy link
Copy Markdown
Contributor

Problem

Reading Axon Framework 4 events/snapshots that were stored without a revision crashes under AF5 with:

java.lang.IllegalArgumentException: The given version is unsupported because it is empty.
    at org.axonframework.messaging.core.MessageType.<init>(MessageType.java:64)
    ...

Reported in #4625 (and hit by at least one other user). AF4 never required a revision — SimpleSerializedType allowed a null revision and Axon Server stores an absent revision as an empty String. AF5's MessageType compact constructor instead rejects both null and "" versions, so any read path that rebuilds a MessageType from stored data (new MessageType(payloadType, payloadRevision)) blows up on legacy data.

What this (DRAFT) PR contains

Only a reproduction — no fix yet. It adds unit tests pinning the root cause: MessageType's compact constructor rejecting an empty version, a null version, and the exact new MessageType(name, "") call the storage engines make.

MessageTypeTest.GivenStoredMessageWithoutVersion — ✅ passing (asserts the current, broken behavior).

The companion runtime reproduction (store an event without a revision in Axon Server, then read it back) lives in the connector repo's matching fix/4625_MessageWithoutRevision branch.

These tests are written as acceptance gates: once a fix is agreed, the assertions flip from "expect the exception" to "read succeeds with the resolved version."

Solution — under discussion ⚠️

The obvious fix is to default an absent (null/empty) version to MessageType.DEFAULT_VERSION ("0.0.1") on read. But that may not be safe for everyone, and that's the open question:

  • Defaulting null/"""0.0.1" is lossy. Applications that already, deliberately, treat "no revision" (legacy/AF4) differently from an explicit "0.0.1" would suddenly see legacy data masquerading as 0.0.1. The two become indistinguishable. Today the only behavioral consumer of version is the snapshot compatibility gate (SnapshottingEntityLifecycleHandler), which degrades safely (mismatch → full replay) — but we shouldn't assume version stays purely informational forever, or that downstream apps see it that way.
  • Candidate shapes still being weighed:
    1. Loosen the compact constructor — normalize null/""DEFAULT_VERSION. One-place fix; every read path inherits it; removes fail-fast on a genuinely missing version in new code.
    2. Factory / string helper on MessageType (e.g. ofNullableVersion / versionOrDefault) — keeps the constructor strict; each read site opts in.
    3. Dedicated legacy factory (e.g. LegacyMessageTypes.messageType(type, revision)) — strongest "legacy only" signal; but the read paths are normal framework code, so the Legacy* label is debatable.
    4. A distinct legacy sentinel instead of reusing 0.0.1, so legacy data stays distinguishable from genuinely-0.0.1 data (trade-off: legacy snapshots would never be trusted by the version gate).

Feedback welcome on which direction to take before this leaves draft.

Relates to #4625.

Pin the current behavior where MessageType's compact constructor rejects
an empty or null version. This is the root cause of #4625 when reading
Axon Framework 4 events/snapshots that were stored without a revision.

No fix applied yet; the solution is under discussion.
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 2, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant