github/copilot-sdk · error · IllegalArgumentException
clazz must not be null
Error message
clazz must not be null
What it means
Thrown by ToolDefinition.fromClass when the clazz argument is null. fromClass scans a class's declared methods for @CopilotTool annotations (without needing an instance); a null Class gives nothing to scan, so it is rejected with this IllegalArgumentException. Note fromClass also rejects classes mixing static and instance @CopilotTool methods.
Solutions
- Verify the class exists and loads before calling: Class.forName(name) with exception handling, then pass its non-null result
- Null-check the Class with Objects.requireNonNull(clazz) at the call site
- Fix configuration/classpath so the tool class is present
- Catch IllegalArgumentException at startup and report which configured class failed to register
Example fix
// before Class<?> c = Class.forName(cfg.toolClass); // throws elsewhere or yields null path ToolDefinition.fromClass(c); // after Class<?> c = Class.forName(cfg.toolClass); // let CNFE surface here Objects.requireNonNull(c); ToolDefinition.fromClass(c);
Defensive patterns
Strategy: validation
Validate before calling
Objects.requireNonNull(clazz, "tool class must not be null"); Class.forName(clazz.getName());
Type guard
boolean canScan(Class<?> c) { return c != null; } Try / catch
try { defs = ToolDefinition.fromClass(clazz); } catch (IllegalArgumentException e) { throw new ToolRegistrationException(String.valueOf(clazz), e); } Prevention
- Resolve configured class names with Class.forName and handle ClassNotFoundException explicitly
- Remove registration entries for deleted tool classes
- Null-check configuration-derived Class references before scanning
When it happens
Trigger: Calling ToolDefinition.fromClass(null) — e.g. Class.forName failed earlier and its result (or a caught ClassNotFoundException path) left the reference null, or a config-specified class name resolved to nothing.
Common situations: Config-driven tool registration where the class name string is wrong or the class isn't on the classpath; refactors deleting the tool class while registration entries remain.
Related errors
- instance must not be null
- fromClass() requires all @CopilotTool methods to be static…
- A Param descriptor is null for tool ' + toolName + '
- Tool name must not be null or blank
- Tool description must not be null or blank
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/e85763a23de1a589.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/rpc/ToolDefinition.java:314
/**
* Discovers tool definitions from a class with static
* {@code @CopilotTool}-annotated methods. Requires that the
* {@code CopilotToolProcessor} annotation processor ran at compile time
* (generating the {@code $$CopilotToolMeta} companion class).
*
* @param clazz
* the class containing static {@code @CopilotTool}-annotated methods
* @return list of tool definitions with working invocation handlers
* @throws IllegalStateException
* if the generated {@code $$CopilotToolMeta} class is not found
* (annotation processor did not run)
* @since 1.0.6
*/
@CopilotExperimental
public static List<ToolDefinition> fromClass(Class<?> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("clazz must not be null");
}
List<String> instanceMethods = Arrays.stream(clazz.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(com.github.copilot.tool.CopilotTool.class))
.filter(m -> !Modifier.isStatic(m.getModifiers())).map(Method::getName).collect(Collectors.toList());
if (!instanceMethods.isEmpty()) {
throw new IllegalArgumentException(
"fromClass() requires all @CopilotTool methods to be static, but found instance methods: "
+ instanceMethods + ". Use fromObject(new " + clazz.getSimpleName() + "()) instead.");
}
return loadDefinitions(clazz, null);
}
// ------------------------------------------------------------------
// Fluent copy-style modifier methods for lambda-defined tools
// ------------------------------------------------------------------
/**
* Returns a copy with the {@code overridesBuiltInTool} flag set.View on GitHub (pinned to cd8cf15dc3)