Skip to content

SkillToolset default SystemInstruction documents skill_name= for load_skill, but the schema expects name= #912

@twoodall-sophos

Description

@twoodall-sophos

Summary

ADK v1.3.0's tool/skilltoolset ships a default SystemInstruction that tells the LLM to call load_skill with a parameter named skill_name, but the tool's actual LoadSkillArgs struct uses name. The LLM follows the documented instruction and is rejected by ADK's own input-schema validator on every session that loads a skill — typically self-correcting only on retry.

A secondary issue: the sibling tool load_skill_resource actually does use skill_name (plus resource_path), so the two tools' parameter naming diverges. Even after fixing the default instruction, this inconsistency is a footgun.

The contradiction

tool/skilltoolset/toolset.go — the default instruction:

defaultSkillSystemInstruction = `... ` +
    "1. If a skill seems relevant to the current user query, you MUST use the `load_skill` " +
    "tool with `skill_name=\"<SKILL_NAME>\"` to read its full instructions before proceeding.\n" +
    ...

tool/skilltoolset/internal/skilltool/load_skill.go — the args:

type LoadSkillArgs struct {
    Name string `json:"name" jsonschema:"The name of the skill to load."`
}

The JSON schema derived from this struct lists name as the only property — so the LLM-emitted call following the default instruction is rejected with unexpected additional properties [skill_name].

Observed behavior

Trace from a test session, in order:

[tool] load_skill {"skill_name":"generate-playbook"}
→ error: validating root: unexpected additional properties ["skill_name"]

[tool] load_skill {"name":"generate-playbook"}     # self-correct
→ success (returns the frontmatter + instructions)

The self-correction tends to work because the response field is named skill_name (LoadSkillResult.SkillName) and the model's pattern flips. But every fresh session pays the cost.

Compounding inconsistency

load_skill_resource.go:

type LoadSkillResourceArgs struct {
    SkillName    string `json:"skill_name"    jsonschema:"The name of the skill."`
    ResourcePath string `json:"resource_path" jsonschema:"..."`
}

So:

  • load_skillname
  • load_skill_resourceskill_name

Even when the default instruction is fixed, callers will keep being confused — the same logical thing (the skill name) has different parameter keys across two co-resident tools.

Suggested fix

Option A (minimal, non-breaking): change the default instruction's reference from skill_name="<SKILL_NAME>" to name="<SKILL_NAME>". One line in toolset.go. Eliminates the misdirection without touching schemas.

Option B (more consistent, breaking for direct callers of load_skill_resource): also rename LoadSkillResourceArgs.SkillNameName (with json tag name) so both tools share the same parameter key. Update the default instruction accordingly.

Happy to send a PR for Option A in the meantime.

Workaround

For now, downstream agents can override the default by passing their own SystemInstruction:

ts, err := skilltoolset.New(ctx, skilltoolset.Config{
    Source:            source,
    SystemInstruction: corrected, // mentions `name=` for load_skill, `skill_name=`+`resource_path=` for load_skill_resource
})

Discovered while building a Bedrock-Claude agent on ADK v1.3.0 — the misdirection cost about 2s per session and confused the model's parameter-name inference in subsequent tool calls on the same conversation.

Metadata

Metadata

Labels

bugSomething isn't working

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions