github/copilot-sdk · error · IllegalArgumentException
Tool description must not be null or blank
Error message
Tool description must not be null or blank
What it means
Tool descriptions are sent to the LLM so it can decide when/how to call a tool; a null or blank description yields an unusable tool. requireNonBlankDescription throws this IllegalArgumentException when the description supplied to any of the from* factory methods is null or whitespace-only.
Solutions
- Provide a concise non-empty description of what the tool does to the from* call.
- If the description comes from configuration/resources, validate non-blank before registering.
- Use the @CopilotTool(description=...) attribute or fromObject/fromClass so descriptions come from annotations consistently.
Example fix
// before
ToolDefinition.from("search", "", handler);
// after
ToolDefinition.from("search", "Searches indexed documents for a keyword query and returns matches.", handler); Defensive patterns
Strategy: validation
Validate before calling
if (description == null || description.isBlank()) throw new IllegalArgumentException("tool description required"); Try / catch
try { return ToolDefinition.from(name, desc, handler); } catch (IllegalArgumentException e) {
log.error("tool '{}' rejected: {}", name, e.getMessage()); throw e;
} Prevention
- Always write a one-sentence behavioral description when registering a tool.
- Centralize tool registration in one factory that enforces name+description checks.
- For annotation-based tools, always set @CopilotTool(description=...).
When it happens
Trigger: Calling ToolDefinition.from/fromAsync/fromWithToolInvocation/fromAsyncWithToolInvocation with description null, "", or " " — often a variable populated from config or a missing constant.
Common situations: Programmatic tool registration from a config file where the description field is absent; leaving description empty while scaffolding a new tool; i18n resource missing so the lookup returns empty string.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Tool name must not be null or blank
- handler must not be null for tool ' + toolName + '
- CliUrl is mutually exclusive with CliPath
- TcpConnectionToken must be a non-empty string
- Invalid value ' '. Expected 'inprocess', 'stdio', or unset.
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/564edff88b400b3c.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/rpc/ToolDefinition.java:914
return mapper.writeValueAsString(result);
} catch (com.fasterxml.jackson.core.JsonProcessingException ex) {
throw new IllegalStateException("Failed to serialize tool result to JSON", ex);
}
}
// ------------------------------------------------------------------
// Validation helpers
// ------------------------------------------------------------------
private static void requireNonBlankToolName(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Tool name must not be null or blank");
}
}
private static void requireNonBlankDescription(String description) {
if (description == null || description.isBlank()) {
throw new IllegalArgumentException("Tool description must not be null or blank");
}
}
private static void requireNonNullHandler(Object handler, String toolName) {
if (handler == null) {
throw new IllegalArgumentException("handler must not be null for tool '" + toolName + "'");
}
}
@SuppressWarnings("unchecked")
private static List<ToolDefinition> loadDefinitions(Class<?> clazz, Object instance) {
String metaClassName = clazz.getName() + "$$CopilotToolMeta";
try {
Class<?> metaClass = Class.forName(metaClassName, true, clazz.getClassLoader());
var provider = (com.github.copilot.tool.CopilotToolMetadataProvider<Object>) metaClass
.getDeclaredConstructor().newInstance();
return provider.definitions(instance, getConfiguredMapper());
} catch (ClassNotFoundException e) {View on GitHub (pinned to cd8cf15dc3)