mudler/LocalAI · error · ValueError

reward_funcs must be a JSON array of reward function specs

Error message

reward_funcs must be a JSON array of reward function specs

What it means

build_reward_functions accepts the reward spec either as a pre-parsed list or as a JSON string; after json.loads (or direct use), the top level must be a JSON array. Passing a single object, a dict, or a double-encoded JSON string ('\"[...]\"' which loads to a str) fails this isinstance check.

Source

Thrown at backend/python/trl/reward_functions.py:211

# Dispatcher
# ---------------------------------------------------------------------------

def build_reward_functions(specs_json):
    """Parse a JSON list of reward function specs and return a list of callables.

    Each spec is a dict with:
      - type: "builtin" or "inline"
      - name: function name
      - code: (inline only) Python function body
      - params: (optional) dict of string params applied via functools.partial
    """
    if isinstance(specs_json, str):
        specs = json.loads(specs_json)
    else:
        specs = specs_json

    if not isinstance(specs, list):
        raise ValueError("reward_funcs must be a JSON array of reward function specs")

    reward_funcs = []
    for spec in specs:
        spec_type = spec.get("type", "builtin")
        name = spec.get("name", "")
        params = spec.get("params", {})

        if spec_type == "builtin":
            if name not in BUILTIN_REGISTRY:
                available = ", ".join(sorted(BUILTIN_REGISTRY.keys()))
                raise ValueError(
                    f"Unknown builtin reward function '{name}'. Available: {available}"
                )
            func = BUILTIN_REGISTRY[name]
            if params:
                # Convert string params to appropriate types
                typed_params = {}
                for k, v in params.items():

View on GitHub (pinned to 44413a9d06)

Solutions

  1. Wrap the spec in a list: '[{"type":"builtin","name":"format_reward"}]'.
  2. If constructing programmatically, pass the Python list directly (build_reward_functions accepts both) instead of ad-hoc string building.
  3. Validate with json.loads(spec) in a unit test and assert isinstance(result, list).

Example fix

# before
extra["reward_funcs"] = json.dumps({"type": "builtin", "name": "format_reward"})
# after
extra["reward_funcs"] = json.dumps([{ "type": "builtin", "name": "format_reward" }])
Defensive patterns

Strategy: validation

Validate before calling

import json

def reward_specs_valid(specs) -> bool:
    parsed = json.loads(specs) if isinstance(specs, str) else specs
    return isinstance(parsed, list)

Type guard

def is_reward_spec_list(x) -> bool:
    import json
    p = json.loads(x) if isinstance(x, str) else x
    return isinstance(p, list) and all(isinstance(s, dict) for s in p)

Try / catch

try:
    build_reward_functions(extra["reward_funcs"])
except ValueError as e:
    if "JSON array" in str(e):
        extra["reward_funcs"] = json.dumps([json.loads(extra["reward_funcs"])])
        retry
    raise

Prevention

When it happens

Trigger: extra_options['reward_funcs'] = '{\"type\": ...}' (single object, not array); reward_funcs='\"[ {...} ]\"' (JSON string of a JSON string); passing a bare function name string like 'format_reward'.

Common situations: Building the extra_options dict in code and forgetting json.dumps of the list; hand-writing JSON in YAML model configs; clients that stringify twice.

Related errors


AI-assisted analysis of mudler/LocalAI@44413a9d06 (2026-08-15). Data as JSON: /api/errors/9f55212b2fe60811. Report an issue: GitHub.