Repository navigation
Resubmitting a publication whose event cannot be deserialized fails application startup #1904
Description
With spring.modulith.events.republish-outstanding-events-on-restart=true, a single event publication whose event can no longer be deserialized (in our case a polymorphic type id changed between releases) keeps the application from starting.
DefaultEventPublicationRegistry.processPublications(…) wraps inProgress.register(it) in a try block. The JDBC publication deserializes its event lazily in getEvent(), and registering builds the in-progress key from that event, so the deserialization failure lands in the catch block. The catch block then calls inProgress.unregister(it), which builds the same key and calls getEvent() again. This second exception escapes the catch block and afterSingletonsInstantiated():
tools.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class com.opotopo.core.audit.AuditEventMetadata]: missing type id property 'kind' (for POJO property 'metadata')
at org.springframework.modulith.events.jackson.JacksonEventSerializer.deserialize(JacksonEventSerializer.java:61)
at org.springframework.modulith.events.jdbc.JdbcEventPublicationRepositoryV2.lambda$resultSetToPublication$1(JdbcEventPublicationRepositoryV2.java:607)
at org.springframework.modulith.events.jdbc.JdbcEventPublicationRepositoryV2$JdbcEventPublication.getEvent(JdbcEventPublicationRepositoryV2.java:720)
at org.springframework.modulith.events.core.DefaultEventPublicationRegistry$PublicationsInProgress$Key.<init>(DefaultEventPublicationRegistry.java:471)
at org.springframework.modulith.events.core.DefaultEventPublicationRegistry$PublicationsInProgress.unregister(DefaultEventPublicationRegistry.java:440)
at org.springframework.modulith.events.core.DefaultEventPublicationRegistry.lambda$processPublications$8(DefaultEventPublicationRegistry.java:331)
at org.springframework.modulith.events.core.DefaultEventPublicationRegistry.processPublications(DefaultEventPublicationRegistry.java:316)
at org.springframework.modulith.events.core.DefaultEventPublicationRegistry.processIncompletePublications(DefaultEventPublicationRegistry.java:220)
at org.springframework.modulith.events.support.PersistentApplicationEventMulticaster.doResubmitUncompletedPublicationsOlderThan(PersistentApplicationEventMulticaster.java:223)
at org.springframework.modulith.events.support.PersistentApplicationEventMulticaster.resubmitIncompletePublications(PersistentApplicationEventMulticaster.java:155)
at org.springframework.modulith.events.support.PersistentApplicationEventMulticaster.afterSingletonsInstantiated(PersistentApplicationEventMulticaster.java:201)
The service restarted in a loop until the row was deleted by hand. Resubmission at runtime (FailedEventPublications.resubmit(…), IncompleteEventPublications.resubmitIncompletePublications(…)) goes through the same code and stops at such a publication, so the rest of the batch isn't resubmitted either.
Seen on 2.1.1 with the JDBC registry, main has the same code. Unregistering only a publication that was actually registered fixes it, PR follows.
Activity
odrotbohm commentedon Oct 1, 2026
This similarly affects the order of processing as GH-1906. I.e. I'd only like to proceed with this one after we're fully aware of the consequences. Having to explicitly decide what kind of ordering guarantees you'd like might be another argument to eventually discontinue the
republish-outstanding-events-on-restartflag entirely.AdzerKI commentedon Oct 9, 2026
#1905 does not change the order.
processPublications(…)already logs a failed resubmission and goes on with the next publication, that is what the catch block is for. An event that cannot be deserialized only breaks out of the loop because unregistering deserializes it a second time and that exception escapes the catch block. With #1905 it fails like any other publication of the run.char-yb's point in #1906 is a separate one: such a publication never reaches a listener, so the
AbandonPolicynever sees it.