{"record":{"id":"76a71ac81fd5e097","repo":"mudler/LocalAI","slug":"inline-reward-function-name-must-return-a-list","errorCode":null,"errorMessage":"Inline reward function '{name}' must return a list, got {type(result).__name__}","messagePattern":"Inline reward function '(.+?)' must return a list, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/python/trl/reward_functions.py","lineNumber":180,"sourceCode":"        \"re\": re,\n        \"math\": math,\n        \"json\": json,\n        \"string\": string,\n    }\n\n    try:\n        compiled = compile(func_source, f\"<inline-reward-{name}>\", \"exec\")\n    except SyntaxError as e:\n        raise ValueError(f\"Syntax error in inline reward function '{name}': {e}\")\n\n    exec(compiled, restricted_globals)\n    func = restricted_globals[f\"_user_reward_{name}\"]\n\n    # Validate with a quick smoke test\n    try:\n        result = func([\"test\"], answer=[\"test\"])\n        if not isinstance(result, list):\n            raise ValueError(\n                f\"Inline reward function '{name}' must return a list, got {type(result).__name__}\"\n            )\n    except Exception as e:\n        if \"must return a list\" in str(e):\n            raise\n        # Other errors during smoke test are acceptable (e.g. missing kwargs)\n        pass\n\n    return func\n\n\n# ---------------------------------------------------------------------------\n# Dispatcher\n# ---------------------------------------------------------------------------\n\ndef build_reward_functions(specs_json):\n    \"\"\"Parse a JSON list of reward function specs and return a list of callables.\n","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/backend/python/trl/reward_functions.py#L162-L198","documentation":"After exec'ing an inline reward, the backend smoke-tests it with func([\"test\"], answer=[\"test\"]) and requires the return value to be a list (TRL's reward contract: one float per completion). Returning a scalar, numpy array, tuple, or None triggers this ValueError naming the actual type; note the 'must return a list' check re-raises even inside the broad except.","triggerScenarios":"Inline code returning sum(scores) instead of scores; returning a numpy array (isinstance(np.array, list) is False); early return None on an edge case; generator expression returned unmaterialized.","commonSituations":"Users porting reward code from single-sample scripts that returned one float; forgetting TRL batches completions.","solutions":["Return a list with one value per completion: return [score for c in completions].","If using numpy, convert: return scores.tolist().","Keep the length equal to len(completions); wrap accidental scalars as [value] only when there is exactly one completion."],"exampleFix":"# before\ndef _user_reward_acc(c, **kw):\n    return 1.0 if c[0] == kw[\"answer\"][0] else 0.0\n# after\ndef _user_reward_acc(c, **kw):\n    return [1.0 if x == a else 0.0 for x, a in zip(c, kw[\"answer\"])]","handlingStrategy":"validation","validationCode":"def reward_returns_list(func, sample=(\"test\",)) -> bool:\n    try:\n        out = func(list(sample), answer=list(sample))\n    except Exception:\n        return True  # smoke errors are tolerated by the backend\n    return isinstance(out, list)","typeGuard":"from typing import Any, Callable\n\ndef is_list_returning_reward(f: Callable) -> bool:\n    out = f([\"test\"], answer=[\"test\"])\n    return isinstance(out, list)","tryCatchPattern":"try:\n    funcs = build_reward_functions(specs)\nexcept ValueError as e:\n    if \"must return a list\" in str(e):\n        raise ConfigError(\"reward must return one float per completion\") from e\n    raise","preventionTips":["Conform to TRL's contract: (completions, **kwargs) -> list[float] with len == len(completions).","Call .tolist() on numpy results.","Write a unit test per custom reward asserting list type and length."],"tags":["trl","reward-functions","inline-code","type-mismatch","localai"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}