spring-cloud / spring-cloud/spring-cloud-stream

Custom Event Cannot be Deserialized

Open
#3,162 14 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Java
Stars
1.1k
Forks
646
Avg merge
2d 3h
Merged PRs (30d)
8

Description

Describe the bug
After upgrading Spring Boot to 4 and Spring Cloud to 2025.1.0, I am getting similar error in logs as spring-cloud/spring-cloud-bus#284 but in my case for custom events. I can confirm, with Spring Boot 3.5.7 and Spring Cloud 2025.0.0 it works as expected and no error thrown. I am adding samples as below, in my current setup event publisher and listener are different spring boot applications connected to the same RabbitMQ.
P.S: First I bumped versions for the event publisher, and listener was able to receive custom events without any problem. After bumped versions for event listener, it started failing to deserialize.

Sample

Event class definition:

import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;

@EqualsAndHashCode(callSuper = true)
public class TestEvent extends RemoteApplicationEvent {
  @Getter
  private String key;

  public TestEvent() {
  }

  public TestEvent(Object source, String originService, String destination, String key) {
    super(source, originService, () -> destination);
    this.key = key;
  }
}

Spring boot application entrypoint:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

@SpringBootApplication
@EnableMongoRepositories
@ComponentScan(basePackages = {"org.test"})
@EnableMongoAuditing(auditorAwareRef = "auditorProvider")
@RemoteApplicationEventScan(basePackages = {"org.test"})
@EnableFeignClients
public class TestServiceApplication {
  public static void main(String[] args) {
    SpringApplication.run(TestServiceApplication.class, args);
  }
}

Event listener:

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class TestEventListener {
  @EventListener
  public void onTestEvent(TestEvent event) {
    log.info("Received TestEvent {}", event);
  }

Event publisher:

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.bus.BusProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class PublisherService {
  private final ApplicationEventPublisher applicationEventPublisher;
  private final BusProperties busProperties;

  protected void publishEvent(String destination, String key) {
    log.debug("Publishing test event with destination {} and key {}", destination, key);
    TestEvent testEvent = new TestEvent(this, busProperties.getId(), String.format("%s:**", destination), key);
    applicationEventPublisher.publishEvent(testEvent);
  }
}

Error logs

2025-12-02T08:34:15.636+03:00 ERROR 64774 --- [test-service] [uCnbIRZGKBk_Q-1] o.s.c.b.j.BusJacksonMessageConverter     : Cannot construct instance of `org.test.model.event.TestEvent`, problem: null source
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: spring-cloud/spring-cloud-bus#234]

tools.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `org.test.model.event.TestEvent`, problem: null source
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: spring-cloud/spring-cloud-bus#234]
	at tools.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:44) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:2080) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.std.StdValueInstantiator.wrapAsDatabindException(StdValueInstantiator.java:581) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:602) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:289) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:270) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.bean.PropertyBasedCreator.build(PropertyBasedCreator.java:252) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.bean.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:697) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.bean.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1417) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.bean.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:480) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.bean.BeanDeserializer._deserializeOther(BeanDeserializer.java:235) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.bean.BeanDeserializer.deserialize(BeanDeserializer.java:202) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:138) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:103) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:243) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:72) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.DeserializationContextExt.readRootValue(DeserializationContextExt.java:265) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2610) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1627) ~[jackson-databind-3.0.2.jar:3.0.2]
	at org.springframework.cloud.bus.jackson.BusJacksonMessageConverter.convertFromInternal(BusJacksonAutoConfiguration.java:180) ~[spring-cloud-bus-5.0.0.jar:5.0.0]
	at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:179) ~[spring-messaging-7.0.1.jar:7.0.1]
	at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:171) ~[spring-messaging-7.0.1.jar:7.0.1]
	at org.springframework.cloud.function.context.config.SmartCompositeMessageConverter.fromMessage(SmartCompositeMessageConverter.java:75) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputMessageIfNecessary(SimpleFunctionRegistry.java:1428) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputIfNecessary(SimpleFunctionRegistry.java:1180) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.doApply(SimpleFunctionRegistry.java:787) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.apply(SimpleFunctionRegistry.java:627) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.observability.ObservationFunctionAroundWrapper.lambda$nonReactorStream$0(ObservationFunctionAroundWrapper.java:50) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at io.micrometer.observation.Observation.observe(Observation.java:564) ~[micrometer-observation-1.16.0.jar:1.16.0]
	at org.springframework.cloud.function.observability.ObservationFunctionAroundWrapper.nonReactorStream(ObservationFunctionAroundWrapper.java:50) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.observability.ObservationFunctionAroundWrapper.doApply(ObservationFunctionAroundWrapper.java:45) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.context.catalog.FunctionAroundWrapper.apply(FunctionAroundWrapper.java:44) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.apply(SimpleFunctionRegistry.java:624) ~[spring-cloud-function-context-5.0.0.jar:5.0.0]
	at org.springframework.cloud.stream.function.PartitionAwareFunctionWrapper.apply(PartitionAwareFunctionWrapper.java:92) ~[spring-cloud-stream-5.0.0.jar:5.0.0]
	at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionWrapper.apply(FunctionConfiguration.java:830) ~[spring-cloud-stream-5.0.0.jar:5.0.0]
	at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder$1.handleMessageInternal(FunctionConfiguration.java:668) ~[spring-cloud-stream-5.0.0.jar:5.0.0]
	at org.springframework.integration.handler.AbstractMessageHandler.doHandleMessage(AbstractMessageHandler.java:102) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:70) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:133) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:150) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:123) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.channel.AbstractMessageChannel.sendInternal(AbstractMessageChannel.java:438) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.channel.AbstractMessageChannel.sendWithMetrics(AbstractMessageChannel.java:409) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:338) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:310) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:187) ~[spring-messaging-7.0.1.jar:7.0.1]
	at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:166) ~[spring-messaging-7.0.1.jar:7.0.1]
	at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47) ~[spring-messaging-7.0.1.jar:7.0.1]
	at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108) ~[spring-messaging-7.0.1.jar:7.0.1]
	at org.springframework.integration.endpoint.MessageProducerSupport.sendMessageWithTracking(MessageProducerSupport.java:278) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:267) ~[spring-integration-core-7.0.0.jar:7.0.0]
	at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter.access$000(AmqpInboundChannelAdapter.java:71) ~[spring-integration-amqp-7.0.0.jar:7.0.0]
	at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.lambda$onMessage$0(AmqpInboundChannelAdapter.java:392) ~[spring-integration-amqp-7.0.0.jar:7.0.0]
	at org.springframework.core.retry.RetryTemplate.execute(RetryTemplate.java:142) ~[spring-core-7.0.1.jar:7.0.1]
	at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.onMessage(AmqpInboundChannelAdapter.java:385) ~[spring-integration-amqp-7.0.0.jar:7.0.0]
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1683) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1612) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1599) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1590) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListenerAndHandleException(AbstractMessageListenerContainer.java:1539) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.lambda$executeListener$1(AbstractMessageListenerContainer.java:1517) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at io.micrometer.observation.Observation.observe(Observation.java:499) ~[micrometer-observation-1.16.0.jar:1.16.0]
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1517) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1116) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1061) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1465) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1359) ~[spring-rabbit-4.0.0.jar:4.0.0]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: java.lang.IllegalArgumentException: null source
	at java.base/java.util.EventObject.<init>(EventObject.java:57) ~[na:na]
	at org.springframework.context.ApplicationEvent.<init>(ApplicationEvent.java:48) ~[spring-context-7.0.1.jar:7.0.1]
	at org.springframework.cloud.bus.event.RemoteApplicationEvent.<init>(RemoteApplicationEvent.java:61) ~[spring-cloud-bus-5.0.0.jar:5.0.0]
	at org.test.model.event.TestEvent.<init>(TestEvent.java:16) ~[classes/:na]
	at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) ~[na:na]
	at tools.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:113) ~[jackson-databind-3.0.2.jar:3.0.2]
	at tools.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:287) ~[jackson-databind-3.0.2.jar:3.0.2]
	... 64 common frames omitted

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at BusJacksonAutoConfiguration.java, especially BusJacksonMessageConverter.convertFromInternal at line 180, and reproduce the failure with the TestEvent, @RemoteApplicationEventScan, and RabbitMQ publisher/listener setup shown here. Compare deserialization between Spring Boot 3.5.7/Spring Cloud 2025.0.0 and the newer versions, then verify that the custom RemoteApplicationEvent is received without the null-source error.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, rabbitmq, spring-boot
Domain
backend, distributed-systems, stream-processing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.