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

  1. Provide a concise non-empty description of what the tool does to the from* call.
  2. If the description comes from configuration/resources, validate non-blank before registering.
  3. 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

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


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)