Skip to content

Resubmitting a publication whose event cannot be deserialized fails application startup #1904

Open

Description

@AdzerKI

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

  1. odrotbohm commented on Oct 1, 2026

    @odrotbohm
    Member

    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-restart flag entirely.

  2. self-assigned this
    on Oct 1, 2026
  3. AdzerKI commented on Oct 9, 2026

    @AdzerKI
    ContributorAuthor

    #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 AbandonPolicy never sees it.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions