alibaba/open-code-review · error

expected '{' in path_rule_map, got %v

Error message

expected '{' in path_rule_map, got %v

What it means

SystemRule.UnmarshalJSON read the first token of path_rule_map successfully but it was not json.Delim('{') — the value is valid JSON yet not an object (e.g. an array, string, or number). The ordered-key streaming parser requires an object because it maps keys (glob patterns) to rule names in encounter order.

Source

Thrown at internal/config/rules/system_rules.go:67

	// Use json.Decoder with UseNumber to preserve order of path_rule_map keys.
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	mapData, ok := raw["path_rule_map"]
	if !ok || len(mapData) == 0 || string(mapData) == "null" {
		return nil
	}

	// Parse ordered keys using a streaming decoder.
	dec := json.NewDecoder(strings.NewReader(string(mapData)))
	// Read opening '{'
	t, err := dec.Token()
	if err != nil {
		return fmt.Errorf("expected '{' in path_rule_map: %w", err)
	}
	if t != json.Delim('{') {
		return fmt.Errorf("expected '{' in path_rule_map, got %v", t)
	}
	for dec.More() {
		// Read key
		keyTok, err := dec.Token()
		if err != nil {
			return fmt.Errorf("read path_rule_map key: %w", err)
		}
		key, ok := keyTok.(string)
		if !ok {
			return fmt.Errorf("expected string key in path_rule_map, got %T", keyTok)
		}
		// Read value
		var value string
		if err := dec.Decode(&value); err != nil {
			return fmt.Errorf("read path_rule_map value for %q: %w", key, err)
		}
		r.PathRules = append(r.PathRules, PathRule{Pattern: key, Rule: value})
	}

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Change path_rule_map to a JSON object: {"<glob pattern>": "<rule file>", ...}
  2. Convert list-of-pairs form [{pattern, rule}] into a map form
  3. Check the config against the expected SystemRule schema for your version
  4. Run 'jq .path_rule_map' on the config — the output must be an object

Example fix

// before
"path_rule_map": [{"pattern": "*.go", "rule": "golang.md"}]
// after
"path_rule_map": {"*.go": "golang.md"}
Defensive patterns

Strategy: validation

Validate before calling

func assertPathRuleMapObject(data []byte) error {
    var probe struct {
        PathRuleMap json.RawMessage `json:"path_rule_map"`
    }
    if err := json.Unmarshal(data, &probe); err != nil { return err }
    if len(probe.PathRuleMap) == 0 || string(probe.PathRuleMap) == "null" { return nil }
    var obj map[string]string
    if err := json.Unmarshal(probe.PathRuleMap, &obj); err != nil {
        return fmt.Errorf("path_rule_map must be an object: %w", err)
    }
    return nil
}

Type guard

func isJSONObject(v json.RawMessage) bool {
    return len(v) > 0 && strings.HasPrefix(strings.TrimSpace(string(v)), "{")
}

Try / catch

if err := json.Unmarshal(cfgData, &systemRule); err != nil {
    if strings.Contains(err.Error(), "expected '{' in path_rule_map") {
        return fmt.Errorf("path_rule_map must be an object of {glob: ruleFile}: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: path_rule_map set to a JSON array, string, number, or boolean in the rules config — e.g. "path_rule_map": ["*.go"] or "path_rule_map": "golang.md" — then the config is unmarshalled into SystemRule.

Common situations: Schema confusion after changing config format between versions; hand-written config using a list of objects instead of a map; YAML/JSON conversion tools that turned a map into a list of pairs.

Related errors


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