alibaba/spring-ai-alibaba · error · IllegalStateException

copy() method did not return a ChatOptions instance

Error message

copy() method did not return a ChatOptions instance

What it means

ChatOptionsProxy.createCopiedProxy deep-copies a ChatOptions via reflection by invoking its copy() method and expects the result to be a ChatOptions. If the implementation's copy() returns something else (a subtype wrapper, null, or a differently typed object), the instanceof check fails and an IllegalStateException is thrown, since the proxy cannot build a safe copy to attach observation metadata to.

Source

Thrown at spring-boot-starters/spring-ai-alibaba-starter-config-nacos/src/main/java/com/alibaba/cloud/ai/agent/nacos/utils/ChatOptionsProxy.java:120

			// 处理父类方法 - 转发到原始对象
			return method.invoke(chatOptions, args);
		}

		private boolean isCopyMethod(Method method) {
			return "copy".equals(method.getName())
					&& method.getParameterCount() == 0
					&& ChatOptions.class.isAssignableFrom(method.getReturnType());
		}

		private Object createCopiedProxy() {
			ChatOptions copiedChatOptions;
			try {
				Method copyMethod = chatOptions.getClass().getMethod("copy");
				// 如果是 private 或 protected,需要 setAccessible(true)
				copyMethod.setAccessible(true);
				Object result = copyMethod.invoke(chatOptions);
				if (!(result instanceof ChatOptions)) {
					throw new IllegalStateException("copy() method did not return a ChatOptions instance");
				}
				copiedChatOptions = (ChatOptions) result;
			}
			catch (NoSuchMethodException e) {
				throw new IllegalStateException("ChatOptions implementation missing copy() method", e);
			}
			catch (Exception e) {
				throw new RuntimeException("Failed to invoke copy() method", e);
			}

			// 创建新的代理对象(深拷贝 metadata)
			return ChatOptionsProxy.createProxy(
					copiedChatOptions,
					new HashMap<>(this.observationMetadata)
			);
		}

		/**

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Fix the ChatOptions implementation so copy() returns a proper ChatOptions (usually 'return new DeepSeekChatOptions(this);' style copy constructor).
  2. Update to the Spring AI version whose ChatOptions API matches what ChatOptionsProxy expects (Spring AI 1.1.x per the BOM).
  3. If copy() cannot be fixed, return the original options instance instead of proxying, or register the options type in ChatOptionsProxy with a dedicated copy strategy.

Example fix

// before
public Object copy() { return toBuilder(); } // returns builder, not ChatOptions
// after
@Override
public MyChatOptions copy() { return new MyChatOptions(this); }
Defensive patterns

Strategy: type-guard

Validate before calling

Object copied = options.getClass().getMethod("copy").invoke(options);
if (!(copied instanceof ChatOptions)) { throw new IllegalStateException("copy() returns non-ChatOptions"); }

Type guard

static boolean hasValidCopy(ChatOptions o) {
    try { return o.copy() instanceof ChatOptions; } catch (Exception e) { return false; }
}

Try / catch

try { return ChatOptionsProxy.createProxy(options, meta); }
catch (IllegalStateException e) {
    if (e.getMessage().contains("did not return a ChatOptions")) { return callWithoutProxy(options, meta); }
    throw e;
}

Prevention

When it happens

Trigger: Creating a ChatOptionsProxy around a ChatOptions implementation whose copy() method returns null or a non-ChatOptions object — typically a custom/legacy ChatOptions class whose copy() signature or return type was changed.

Common situations: Upgrading Spring AI so a custom ChatOptions copy() no longer returns ChatOptions; a hand-written ChatOptions where copy() returns a builder or a record of a different type; copy() throwing internally and returning null via a catch block.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/b90da8400a426098. Report an issue: GitHub.