github/copilot-sdk · error · IllegalArgumentException

handler must not be null for tool ' + toolName + '

Error message

handler must not be null for tool ' + toolName + '

What it means

A ToolDefinition must have an actual handler to execute when the tool is invoked; a null handler would produce a tool that cannot run. requireNonNullHandler throws this IllegalArgumentException when the handler argument to a from* factory is null, including the tool name in the message to help locate the offending registration.

Solutions

  1. Pass a non-null handler (lambda, method reference, or Function/BiFunction) for each tool registration.
  2. Null-check the handler (or assert bean presence) before calling the from* method and fail fast with context.
  3. Fix DI/initialization ordering so the handler object exists before ToolDefinition registration runs.

Example fix

// before
Function<String, String> h = handlers.get("search"); // null if missing
ToolDefinition.from("search", "Search docs", h);

// after
Function<String, String> h = Objects.requireNonNull(handlers.get("search"), "missing search handler");
ToolDefinition.from("search", "Search docs", h);
Defensive patterns

Strategy: type-guard

Validate before calling

Objects.requireNonNull(handler, "handler must not be null");

Type guard

static boolean hasHandler(Object handler) { return handler != null; }

Try / catch

try { return ToolDefinition.from(name, desc, handler); } catch (IllegalArgumentException e) {
  log.error("registration for tool '{}' failed: {}", name, e.getMessage()); throw e;
}

Prevention

When it happens

Trigger: Calling ToolDefinition.from/fromAsync/fromWithToolInvocation/fromAsyncWithToolInvocation with a null handler — typically a method reference or lambda expression that resolved to null, or a nullable field/config-supplied handler.

Common situations: Spring/DI wiring where the handler bean is null at registration time; conditionally-initialized handlers (e.g. behind a feature flag) that were never created; passing a static method reference from a class that failed to load; storing handlers in a map and looking up a missing key.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/7123bdf680ced644. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/rpc/ToolDefinition.java:920

    // ------------------------------------------------------------------
    // 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) {
            throw new IllegalStateException("Generated class " + metaClassName + " not found. "
                    + "Ensure the CopilotToolProcessor annotation processor ran during compilation. "
                    + "Add the copilot-sdk-java dependency to your annotation processor path.", e);
        } catch (ReflectiveOperationException e) {
            throw new IllegalStateException("Failed to invoke " + metaClassName + ".definitions()", e);
        }

View on GitHub (pinned to cd8cf15dc3)