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
- Fix the ChatOptions implementation so copy() returns a proper ChatOptions (usually 'return new DeepSeekChatOptions(this);' style copy constructor).
- Update to the Spring AI version whose ChatOptions API matches what ChatOptionsProxy expects (Spring AI 1.1.x per the BOM).
- 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
- Keep custom ChatOptions copy() implementations returning the concrete options type
- Pin Spring AI versions via the spring-ai-alibaba-bom
- Write a reflection test asserting copy() return types for all ChatOptions classes
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
- ChatOptions implementation missing copy() method
- Failed to invoke copy() method
- chatOptions type ({}) should be consistent with the default
- The provided chatOptions is not of type ToolCallingChatOptio
- unknown component type: + componentType.getValue()
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/b90da8400a426098.
Report an issue: GitHub.