{"record":{"id":"5947c04e3b628eae","repo":"commaai/openpilot","slug":"function-body-is-empty","errorCode":null,"errorMessage":"Function body is empty","messagePattern":"Function body is empty","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"openpilot/tools/jotpluggler/math_eval.py","lineNumber":38,"sourceCode":"def _write_vector(path: str, values: np.ndarray) -> None:\n  np.asarray(values, dtype=np.float64).tofile(path)\n\n\ndef _resample_to_reference(ref_t: np.ndarray, src_t: np.ndarray, src_v: np.ndarray) -> np.ndarray:\n  ref_t = np.asarray(ref_t, dtype=np.float64).reshape(-1)\n  src_t = np.asarray(src_t, dtype=np.float64).reshape(-1)\n  src_v = np.asarray(src_v, dtype=np.float64).reshape(-1)\n  if ref_t.size == 0 or src_t.size == 0 or src_v.size == 0:\n    return np.empty_like(ref_t)\n  indices = np.searchsorted(src_t, ref_t, side=\"right\") - 1\n  indices = np.clip(indices, 0, src_v.size - 1)\n  return src_v[indices]\n\n\ndef _evaluate_user_code(code: str, env: dict):\n  stripped = code.strip()\n  if not stripped:\n    raise ValueError(\"Function body is empty\")\n\n  expr = stripped\n  if expr.startswith(\"return \"):\n    expr = expr[7:].strip()\n  try:\n    return eval(expr, env, env)\n  except SyntaxError:\n    pass\n\n  function_src = \"def __jotpluggler_eval__():\\n\" + textwrap.indent(code, \"    \")\n  exec(function_src, env, env)\n  return env[\"__jotpluggler_eval__\"]()\n\n\ndef main() -> int:\n  if len(sys.argv) != 6:\n    print(\"usage: math_eval.py <manifest.json> <globals.py> <code.py> <out_t.bin> <out_v.bin>\", file=sys.stderr)\n    return 2","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/tools/jotpluggler/math_eval.py#L20-L56","documentation":"jotpluggler's math_eval evaluates user code from the per-metric code file. _evaluate_user_code() strips whitespace and refuses an empty body - there is literally nothing to eval or wrap in a generated function. This is a fast, clear validation failure before exec/eval machinery runs.","triggerScenarios":"A metric's code file is empty or contains only whitespace/newlines; template metric created but never filled in; file path resolution returned an empty file.","commonSituations":"Scaffolding a new custom series and forgetting to write the expression; editor saved an empty buffer; CI fixture generating blank code files.","solutions":["Put an expression or a function body into the code file, e.g. 'return a * 2' or a bare expression using env variables","Check the file actually being read (code_path) - it may not be the file you edited","If the metric is intentionally unused, remove its config entry instead of leaving an empty code file"],"exampleFix":"# before (code file contents)\n\n# after\nreturn src_v * 3.28084  # m/s to ft/s","handlingStrategy":"validation","validationCode":"code = open(code_path, encoding='utf-8').read()\nif not code.strip():\n    raise SystemExit(f\"metric code file {code_path} is empty - write an expression or remove the metric\")","typeGuard":null,"tryCatchPattern":"try:\n    result = _evaluate_user_code(user_code, env)\nexcept ValueError as e:\n    if 'empty' in str(e):\n        raise SystemExit(f'fill in or delete {code_path}')\n    raise","preventionTips":["Reject empty code files at metric-creation time (in scaffolding/tooling), not at eval time","Add a trivial smoke test per metric (eval once with sample env) to CI so blank templates fail early"],"tags":["jotpluggler","user-code","validation","openpilot"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}