google/gson · error · IllegalArgumentException

GSON cannot serialize or deserialize

Error message

GSON cannot serialize or deserialize ${type}

What it means

Thrown by Gson.getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken) when, after skipping past the specified factory, no subsequent factory produces an adapter for the type. This is the path used by custom factories that delegate to the 'next' adapter; if there is no next adapter the type cannot be (de)serialized and an IllegalArgumentException is raised.

Solutions

  1. Register the delegating TypeAdapterFactory BEFORE the built-in/fallback factory it wants to delegate to (use registerTypeAdapterFactory early).
  2. Make sure there is a factory after skipPast able to handle the type.
  3. If you do not actually need delegation, call getAdapter(type) instead of getDelegateAdapter(this, type).
  4. Restructure so the decorator returns a non-null adapter itself rather than relying on a later factory.

Example fix

// before: decorator registered last -> no delegate
new GsonBuilder()
  .registerTypeAdapter(Foo.class, defaultFooAdapter)
  .registerTypeAdapterFactory(decoratorThatDelegates) // last -> throws
  .create();

// after: decorator first so delegate exists after it
new GsonBuilder()
  .registerTypeAdapterFactory(decoratorThatDelegates)
  .registerTypeAdapter(Foo.class, defaultFooAdapter)
  .create();
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the delegating factory is registered before fallbacks
// Order matters: register decorator FIRST, then the adapter it delegates to.
new GsonBuilder()
  .registerTypeAdapterFactory(decorator)   // earlier
  .registerTypeAdapter(Foo.class, fallback) // later -> delegate exists
  .create();

Try / catch

try { gson.getDelegateAdapter(this, type); }
catch (IllegalArgumentException e) {
  if (e.getMessage().contains("cannot serialize or deserialize")) {
    // fall back to getAdapter(type) or restructure factory order
  } else throw e;
}

Prevention

When it happens

Trigger: A custom factory calling gson.getDelegateAdapter(this, type) when it is the last factory in the chain; delegating past a factory for a type only that factory handled; skipPast factory not actually registered (falls back to getAdapter, so this specific throw needs skipPastFound && no later match).

Common situations: Ordering mistake: a delegating factory registered after all relevant built-ins; a decorator factory that should wrap the default adapter but is placed last; manually iterating factories and skipping too far.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/8daba4249f09f47f. Report an issue: GitHub.

Appendix: source

Thrown at gson/src/main/java/com/google/gson/Gson.java:498

    boolean skipPastFound = false;
    for (TypeAdapterFactory factory : factories) {
      if (!skipPastFound) {
        @SuppressWarnings("ReferenceEquality")
        boolean isSkipPast = factory == skipPast;
        if (isSkipPast) {
          skipPastFound = true;
        }
        continue;
      }

      TypeAdapter<T> candidate = factory.create(this, type);
      if (candidate != null) {
        return candidate;
      }
    }

    if (skipPastFound) {
      throw new IllegalArgumentException("GSON cannot serialize or deserialize " + type);
    } else {
      // Probably a factory from @JsonAdapter on a field
      return getAdapter(type);
    }
  }

  /**
   * This method serializes the specified object into its equivalent representation as a tree of
   * {@link JsonElement}s. This method should be used when the specified object is not a generic
   * type. This method uses {@link Class#getClass()} to get the type for the specified object, but
   * the {@code getClass()} loses the generic type information because of the Type Erasure feature
   * of Java. Note that this method works fine if any of the object fields are of generic type, just
   * the object itself should not be of a generic type. If the object is of generic type, use {@link
   * #toJsonTree(Object, Type)} instead.
   *
   * @param src the object for which JSON representation is to be created
   * @return JSON representation of {@code src}.
   * @since 1.4

View on GitHub (pinned to 310ac341f2)