lysine-dev/retrofit · error · NullPointerException

mapper == null

Error message

mapper == null

What it means

Thrown by JacksonConverterFactory.create(ObjectMapper, MediaType) as an explicit NullPointerException when the supplied ObjectMapper is null. The factory holds the mapper for all subsequent conversions; failing at construction surfaces the misconfiguration immediately rather than as an obscure NPE during the first request.

Source

Thrown at retrofit-converters/jackson/src/main/java/retrofit2/converter/jackson/JacksonConverterFactory.java:56

 */
public final class JacksonConverterFactory extends Converter.Factory {
  private static final MediaType DEFAULT_MEDIA_TYPE =
      MediaType.get("application/json; charset=UTF-8");

  /** Create an instance using a default {@link ObjectMapper} instance for conversion. */
  public static JacksonConverterFactory create() {
    return new JacksonConverterFactory(new ObjectMapper(), DEFAULT_MEDIA_TYPE, false);
  }

  /** Create an instance using {@code mapper} for conversion. */
  public static JacksonConverterFactory create(ObjectMapper mapper) {
    return create(mapper, DEFAULT_MEDIA_TYPE);
  }

  /** Create an instance using {@code mapper} and {@code mediaType} for conversion. */
  @SuppressWarnings("ConstantConditions") // Guarding public API nullability.
  public static JacksonConverterFactory create(ObjectMapper mapper, MediaType mediaType) {
    if (mapper == null) throw new NullPointerException("mapper == null");
    if (mediaType == null) throw new NullPointerException("mediaType == null");
    return new JacksonConverterFactory(mapper, mediaType, false);
  }

  private final ObjectMapper mapper;
  private final MediaType mediaType;
  private final boolean streaming;

  private JacksonConverterFactory(ObjectMapper mapper, MediaType mediaType, boolean streaming) {
    this.mapper = mapper;
    this.mediaType = mediaType;
    this.streaming = streaming;
  }

  /**
   * Return a new factory which streams serialization of request messages to bytes on the HTTP thread
   * This is either the calling thread for {@link Call#execute()}, or one of OkHttp's background
   * threads for {@link Call#enqueue}. Response bytes are always converted to message instances on

View on GitHub (pinned to d0b112dad0)

Solutions

  1. Pass a non-null ObjectMapper: use JacksonConverterFactory.create() for a default instance, or create(new ObjectMapper()) / create(objectMapper, mediaType).
  2. If using DI, ensure the ObjectMapper @Provides/@Bean binding is present and returns non-null.
  3. Initialize the ObjectMapper field before constructing the factory.
  4. In tests, pass a real ObjectMapper or a properly stubbed non-null mock.

Example fix

// before
ObjectMapper mapper = diContainer.get(ObjectMapper.class); // null when unbound
Retrofit retrofit = new Retrofit.Builder()
    .addConverterFactory(JacksonConverterFactory.create(mapper))
    .build();

// after
ObjectMapper mapper = diContainer.get(ObjectMapper.class);
if (mapper == null) mapper = new ObjectMapper();
Retrofit retrofit = new Retrofit.Builder()
    .addConverterFactory(JacksonConverterFactory.create(mapper))
    .build();
Defensive patterns

Strategy: validation

Validate before calling

ObjectMapper mapper = resolveObjectMapper();
if (mapper == null) {
  throw new IllegalStateException("ObjectMapper must not be null before building JacksonConverterFactory");
}
JacksonConverterFactory factory = JacksonConverterFactory.create(mapper);

Type guard

// Kotlin: non-nullable parameter forces the compiler to reject null at the call site.
fun converterFactory(mapper: ObjectMapper): JacksonConverterFactory =
    JacksonConverterFactory.create(mapper)

Try / catch

JacksonConverterFactory factory;
try {
  factory = JacksonConverterFactory.create(resolveObjectMapper());
} catch (NullPointerException e) {
  if ("mapper == null".equals(e.getMessage())) {
    factory = JacksonConverterFactory.create(); // fall back to default ObjectMapper
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling JacksonConverterFactory.create(null) or create(null, mediaType) directly, or passing a null reference from an unbound DI provider, an uninitialized field, or a test mock that returned null.

Common situations: A Spring/Dagger bean that injects an ObjectMapper which was never provided, a Kotlin lateinit ObjectMapper field read before initialization, or a test wiring the factory with a null value by accident.

Related errors


AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13). Data as JSON: /api/errors/388c590410526a14. Report an issue: GitHub.