alibaba/open-code-review · error

unmarshal tools file

Error message

unmarshal tools file: %w

What it means

Load successfully read the tools JSON but json.Unmarshal could not decode it into []ToolConfigEntry; the error wraps the decode failure. This indicates the file is malformed JSON or its structure does not match the expected array of tool entries.

Solutions

  1. Run the file through a JSON validator and fix syntax errors
  2. Compare the file structure against []ToolConfigEntry and fix field names/types
  3. Start from the built-in default tools JSON (pass empty path) and re-apply customizations

Example fix

// before
[{"name":"read_files","enabled":"yes"}] // wrong type
// after
[{"name":"read_files","enabled":true}]
Defensive patterns

Strategy: validation

Validate before calling

var probe []toolsconfig.ToolConfigEntry
if err := json.Unmarshal(data, &probe); err != nil { return err } // pre-check before Load

Try / catch

tools, err := toolsconfig.Load(path)
if err != nil {
    var typeErr *json.UnmarshalTypeError
    if errors.As(err, &typeErr) { return fmt.Errorf("tools schema mismatch at %s: %w", typeErr.Field, err) }
    return err
}

Prevention

When it happens

Trigger: Calling Load() (default bytes) or Load(path) where the JSON is syntactically invalid, is not an array, or entries have fields with mismatched types (e.g. string where bool expected).

Common situations: Hand-edited tools file with a trailing comma or unquoted key; a tools file written for an older schema version whose fields changed types; someone saved a YAML file with a .json extension.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/ae5a1429156da9cd. Report an issue: GitHub.

Appendix: source

Thrown at internal/config/toolsconfig/toolsconfig.go:40

//go:embed tools.json
var defaultToolsJSON []byte

// Load parses the tools config file. When path is empty, falls back to
// the embedded default tools configuration.
func Load(path string) ([]ToolConfigEntry, error) {
	var data []byte
	var err error
	if path == "" {
		data = defaultToolsJSON
	} else {
		data, err = os.ReadFile(path)
		if err != nil {
			return nil, fmt.Errorf("read tools file %s: %w", path, err)
		}
	}
	var tools []ToolConfigEntry
	if err := json.Unmarshal(data, &tools); err != nil {
		return nil, fmt.Errorf("unmarshal tools file: %w", err)
	}
	return tools, nil
}

// ToolDefsByPhase returns the parsed tool definitions filtered by phase.
// planOnly=true returns only tools with plan_task:true.
// planOnly=false returns only tools with main_task:true.
func (t *ToolConfigEntry) ToolDefsByPhase(planOnly bool) (json.RawMessage, bool) {
	switch {
	case planOnly && t.PlanTask:
		return t.Definition, true
	case !planOnly && t.MainTask:
		return t.Definition, true
	default:
		return nil, false
	}
}

View on GitHub (pinned to 5cf97d0d15)