OpenFeign/feign · error · IllegalArgumentException

Type must be an interface

Error message

Type must be an interface: ${type}

What it means

Validation in TargetSpecificationVerifier.verify: the type of the given Target is not an interface, so Feign's dynamic-proxy-based client cannot be constructed. Feign creates a JDK Proxy over the target's interface; a class (or primitive/array) type is unsupported input and the target can never be instantiated.

Solutions

  1. Define the API as an interface and point the Target at that interface type
  2. Use Feign.builder().target(TypeApi.class, url) with an interface rather than a concrete class
  3. If you have a concrete class, extract an interface for its remote methods
  4. Check for accidental use of getClass() or an implementation type when constructing the Target
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at core/src/main/java/feign/ReflectiveFeign.java:235 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10). Data as JSON: /api/errors/4c52dfef71943906. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/feign/ReflectiveFeign.java:235

    }

    private MethodHandler createMethodHandler(
        final Target<?> target, final MethodMetadata md, final C requestContext) {
      if (md.isIgnored()) {
        return args -> {
          throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
        };
      }

      return factory.create(target, md, requestContext);
    }
  }

  private static class TargetSpecificationVerifier {
    public static <T> void verify(Target<T> target) {
      Class<T> type = target.type();
      if (!type.isInterface()) {
        throw new IllegalArgumentException("Type must be an interface: " + type);
      }

      for (final Method m : type.getMethods()) {
        final Class<?> retType = m.getReturnType();

        if (!CompletableFuture.class.isAssignableFrom(retType)) {
          continue; // synchronous case
        }

        if (retType != CompletableFuture.class) {
          throw new IllegalArgumentException(
              "Method return type is not CompleteableFuture: "
                  + getFullMethodName(type, retType, m));
        }

        final Type genRetType = m.getGenericReturnType();

        if (!(genRetType instanceof ParameterizedType)) {

View on GitHub (pinned to e2a1e27560)