alibaba/open-code-review · error

unmarshal rule file %s: %w

Error message

unmarshal rule file %s: %w

What it means

loadRuleFile unmarshals the read project rule file into ProjectRule. This error wraps json.Unmarshal's failure and includes the file path: the file exists but its content is not valid JSON or doesn't match the ProjectRule schema.

Source

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

		}
		return nil, fmt.Errorf("read global rule %s: %w", path, err)
	}
	var pr ProjectRule
	if err := json.Unmarshal(data, &pr); err != nil {
		return nil, fmt.Errorf("unmarshal global rule: %w", err)
	}
	resolveRuleEntries(pr.Rules, filepath.Dir(path), "")
	return &pr, nil
}

func loadRuleFile(path string) (*ProjectRule, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("read rule file %s: %w", path, err)
	}
	var pr ProjectRule
	if err := json.Unmarshal(data, &pr); err != nil {
		return nil, fmt.Errorf("unmarshal rule file %s: %w", path, err)
	}
	resolveRuleEntries(pr.Rules, filepath.Dir(path), "")
	return &pr, nil
}

// loadProjectRule reads <repoDir>/.opencodereview/rule.json. Since #287 anchored
// RepoDir at the git top-level, `ocr review` from a monorepo subdirectory loads
// the repo-root rule file — which is consistent, since rule entries match against
// root-relative diff paths. A subproject-local rule.json under the subdirectory is
// intentionally not consulted; put shared rules at the repo root, or pass --rule.
func loadProjectRule(repoDir string) (*ProjectRule, error) {
	confineRoot, err := pathutil.CanonicalPath(repoDir)
	if err != nil {
		return nil, fmt.Errorf("resolve repo dir %s: %w", repoDir, err)
	}

	path := filepath.Join(repoDir, ".opencodereview", "rule.json")
	resolved, err := filepath.EvalSymlinks(path)

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Run jq . <path> to locate the syntax error and fix it
  2. Remove comments and any leading BOM; save as strict UTF-8 JSON matching ProjectRule's shape
  3. Ensure top level is an object with a rules field; validate the whole file before committing it to the repo

Example fix

// before (comments not allowed)
{
  // project rules
  "rules": []
}
// after
{
  "rules": []
}
Defensive patterns

Strategy: validation

Validate before calling

data, err := os.ReadFile(path)
if err != nil { return err }
if len(data) > 2 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF {
	return fmt.Errorf("%s has a UTF-8 BOM; save as plain UTF-8 JSON", path)
}
var v any
if err := json.Unmarshal(data, &v); err != nil {
	return fmt.Errorf("%s is not valid JSON: %v", path, err)
}

Try / catch

pr, err := loadRuleFile(path)
if err != nil {
	if strings.Contains(err.Error(), "unmarshal rule file") {
		var se *json.SyntaxError
		if errors.As(err, &se) { // json.SyntaxError passes through %w
			return fmt.Errorf("%s: JSON syntax at offset %d", path, se.Offset)
		}
	}
	return err
}

Prevention

When it happens

Trigger: Valid read, failed json.Unmarshal — malformed JSON in <repoDir>/.opencodereview/rule.json (commas, comments — JSON has none —, truncated file), or wrong structure (array at top level, unexpected field types).

Common situations: Hand-edited project rule file with a syntax error; JSONC-style comments that strict encoding/json rejects; a file from another tool pasted into rule.json; CRLF/BOM issues from editors (a leading BOM breaks encoding/json).

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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