Skip to content

Agents.UnmarshalYAML silently drops unknown fields despite yaml.Strict() in Parse #1683

@simonferquel

Description

@simonferquel

Bug

latest.Parse() and v4.Parse() call yaml.UnmarshalWithOptions(data, &cfg, yaml.Strict()) to reject unknown fields. However, strict mode is not propagated into agent blocks because Agents.UnmarshalYAML re-marshals each agent value to bytes and then calls plain yaml.Unmarshal — losing the strict option.

Impact

Unknown fields in agent blocks are silently ignored. For example, writing instructions (plural) instead of the correct instruction (singular) produces no error, but the instruction is completely lost — the agent runs with no instructions at all.

Root cause

In both pkg/config/latest/types.go and pkg/config/v4/types.go:

func (c *Agents) UnmarshalYAML(unmarshal func(any) error) error {
    var items yaml.MapSlice
    if err := unmarshal(&items); err != nil {
        return err
    }
    for _, item := range items {
        // ...
        valueBytes, err := yaml.Marshal(item.Value)   // re-marshal to bytes
        // ...
        var agent AgentConfig
        if err := yaml.Unmarshal(valueBytes, &agent); err != nil {  // ← plain Unmarshal, no strict!
            return fmt.Errorf(...)
        }
        // ...
    }
    // ...
}

The yaml.Strict() option from Parse() only applies to the top-level Config struct. Once Agents.UnmarshalYAML re-marshals and re-unmarshals agent values, the strictness is gone.

Reproduction

cfg, err := latest.Parse([]byte(`
version: "5"
agents:
  root:
    model: openai/gpt-4o
    instructions: "You are a helpful assistant."
`))
// err == nil — no error!
// cfg.Agents[0].Instruction == "" — instruction silently lost!

Expected behavior

latest.Parse (and v4.Parse) should return an error like:

unknown field "instructions"

Fix

Change yaml.Unmarshal(valueBytes, &agent) to yaml.UnmarshalWithOptions(valueBytes, &agent, yaml.DisallowUnknownField()) in both latest/types.go and v4/types.go.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions