google/gson · error · JsonSyntaxException

Expected a " + requestedType.getName() + " but was " + resul

Error message

Expected a " + requestedType.getName() + " but was " + result.getClass().getName() + "; at path " + in.getPreviousPath()

What it means

Gson's newTypeHierarchyFactory wraps an adapter so that, after reading, it verifies the result is an instance of the requested type. If the delegate produces a different concrete type, Gson throws JsonSyntaxException 'Expected a <X> but was <Y>'. This catches mismatches in type-hierarchy adapters (e.g. Number, InetAddress, Collection).

Source

Thrown at gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java:1081

      @SuppressWarnings("unchecked")
      @Override
      public <T2> TypeAdapter<T2> create(Gson gson, TypeToken<T2> typeToken) {
        Class<? super T2> requestedType = typeToken.getRawType();
        if (!clazz.isAssignableFrom(requestedType)) {
          return null;
        }
        return (TypeAdapter<T2>)
            new TypeAdapter<T1>() {
              @Override
              public void write(JsonWriter out, T1 value) throws IOException {
                typeAdapter.write(out, value);
              }

              @Override
              public T1 read(JsonReader in) throws IOException {
                T1 result = typeAdapter.read(in);
                if (result != null && !requestedType.isInstance(result)) {
                  throw new JsonSyntaxException(
                      "Expected a "
                          + requestedType.getName()
                          + " but was "
                          + result.getClass().getName()
                          + "; at path "
                          + in.getPreviousPath());
                }
                return result;
              }
            };
      }

      @Override
      public String toString() {
        return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
      }
    };
  }

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Ensure the registered TypeAdapter returns an instance assignable to the requested type.
  2. Register an InstanceCreator or factory for the specific concrete subtype you expect.
  3. Use a concrete target type (e.g. ArrayList instead of Collection) when deserializing.
  4. If you wrote a custom hierarchy adapter, add a runtime check and construct the correct concrete type.

Example fix

// before: adapter for Number returns Double even when Long is requested
Gson g = new GsonBuilder().registerTypeHierarchyAdapter(Number.class, (JsonSerializer<Number>)(s, t, c) -> new JsonPrimitive(s.doubleValue())).create();

// after: register a factory that honors the requested type
Gson g = new GsonBuilder().registerTypeAdapterFactory(new TypeAdapterFactory() {
    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (!Number.class.isAssignableFrom(type.getRawType())) return null;
        return (TypeAdapter<T>) new TypeAdapter<Number>() {
            @Override public Number read(JsonReader in) throws IOException { return in.nextLong(); }
            @Override public void write(JsonWriter out, Number v) throws IOException { out.value(v); }
        };
    }
}).create();
Defensive patterns

Strategy: try-catch

Validate before calling

boolean adapterReturnsAssignable(TypeAdapterFactory f, Gson g, TypeToken<?> requested) {
  // smoke-test: build adapter and parse a sample; verify instance type
  return true; // real validation is integration testing the factory
}

Type guard

static boolean isAssignableTo(Class<?> requested, Object result) {
  return result == null || requested.isInstance(result);
}

Try / catch

try {
  T result = gson.fromJson(json, type);
} catch (JsonSyntaxException e) {
  if (e.getMessage().contains("Expected a")) { /* register correct factory or use concrete type */ }
  else throw e;
}

Prevention

When it happens

Trigger: Deserializing a type that uses a hierarchy factory (such as Collection, Number, InetAddress subclasses) where the underlying adapter returns a concrete type that is not assignable to the declared target type, e.g. asking for Set but the factory only ever produces List.

Common situations: Registering a custom adapter for a supertype that returns an incompatible subtype, declaring an interface/abstract type whose only available implementation is not assignable, or a JSON shape that makes the default adapter choose a different concrete class.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/aae138136ccb1284.json. Report an issue: GitHub.