alibaba/spring-ai-alibaba · error · IllegalArgumentException

SkillRegistry must be provided. Use SkillsAgentHook to load

Error message

SkillRegistry must be provided. Use SkillsAgentHook to load skills.

What it means

SkillsInterceptor's private constructor requires a non-null SkillRegistry. The interceptor has nothing to resolve skills against without one, so building without a registry throws IllegalArgumentException and points you to SkillsAgentHook as the intended way to load skills.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/skills/SkillsInterceptor.java:112

 * {@value ReadSkillTool#READ_SKILL}. For each such call, the <i>skill_name</i> argument is recorded.
 * Tools from {@link #getGroupedTools()} for those skill names are then added to the request's
 * {@link ModelRequest#getDynamicToolCallbacks() dynamicToolCallbacks}.
 */
public class SkillsInterceptor extends ModelInterceptor {

	private static final Logger logger = LoggerFactory.getLogger(SkillsInterceptor.class);

	private final SkillRegistry skillRegistry;

	private final Map<String, List<ToolCallback>> groupedTools;

	private final Supplier<Map<String, List<ToolCallback>>> groupedToolsSupplier;

	private final ToolCallbackResolver toolCallbackResolver;

	private SkillsInterceptor(Builder builder) {
		if (builder.skillRegistry == null) {
			throw new IllegalArgumentException("SkillRegistry must be provided. Use SkillsAgentHook to load skills.");
		}
		this.skillRegistry = builder.skillRegistry;
		this.groupedTools = builder.groupedTools != null
				? builder.groupedTools
				: Collections.emptyMap();
		this.groupedToolsSupplier = builder.groupedToolsSupplier;
		this.toolCallbackResolver = builder.toolCallbackResolver;
	}

	public static Builder builder() {
		return new Builder();
	}

	@Override
	public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
		List<SkillMetadata> skills = skillRegistry.listAll();

		if (skills.isEmpty()) {

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Use SkillsAgentHook to load and register skills, which supplies the SkillRegistry
  2. Pass a non-null SkillRegistry to the SkillsInterceptor Builder before build()
  3. Check that the hook that populates the registry actually executed before interceptor creation

Example fix

// before
SkillsInterceptor.builder().build(); // no skillRegistry
// after
new SkillsAgentHook(registryPath).register();
SkillsInterceptor.builder().skillRegistry(registry).build();
Defensive patterns

Strategy: validation

Validate before calling

if (registry == null) { throw new IllegalStateException("SkillRegistry not initialized; run SkillsAgentHook first"); }
SkillsInterceptor.builder().skillRegistry(registry).build();

Try / catch

try { interceptor = SkillsInterceptor.builder().build(); } catch (IllegalArgumentException e) { throw new IllegalStateException("Wire SkillsAgentHook before SkillsInterceptor", e); }

Prevention

When it happens

Trigger: Building a SkillsInterceptor via its Builder without calling the method that sets skillRegistry (or passing null), instead of using SkillsAgentHook to populate it.

Common situations: Manually constructing the interceptor instead of going through SkillsAgentHook; registry wiring removed during refactor; conditional registration where the hook never ran.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/0417a4597b8782cd. Report an issue: GitHub.