{"record":{"id":"16ad0ccd95b42f89","repo":"apache/druid","slug":"javascript-parsed-value-s-must-be-in-key-valu","errorCode":null,"errorMessage":"JavaScript parsed value [%s] must be in {key: value} format!","messagePattern":"JavaScript parsed value \\[(.+?)\\] must be in (.+?) format!","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/java/util/common/parsers/JavaScriptParser.java","lineNumber":77,"sourceCode":"    };\n  }\n\n  private final Function<Object, Object> fn;\n\n  public JavaScriptParser(\n      final String function\n  )\n  {\n    this.fn = compile(function);\n  }\n\n  @Override\n  public Map<String, Object> parseToMap(String input)\n  {\n    try {\n      final Object compiled = fn.apply(input);\n      if (!(compiled instanceof Map)) {\n        throw new ParseException(input, \"JavaScript parsed value [%s] must be in {key: value} format!\", input);\n      }\n\n      return (Map) compiled;\n    }\n    catch (Exception e) {\n      throw new ParseException(input, e, \"Unable to parse row [%s]\", input);\n    }\n  }\n\n  @Override\n  public void setFieldNames(Iterable<String> fieldNames)\n  {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public List<String> getFieldNames()\n  {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/java/util/common/parsers/JavaScriptParser.java#L59-L95","documentation":"JavaScriptParser.parseToMap applies a user-supplied JavaScript function to the input string and expects the function to return a Map<String, Object> representing {key: value} columns. If the function returns something other than a Map (string, number, array, null), the parser raises this ParseException before the outer catch re-wraps it as 'Unable to parse row'.","triggerScenarios":"A JavaScript parser/function config whose script ends with something like 'function(str) { return str; }' or returns an array/scalar instead of an object literal, applied via parseToMap.","commonSituations":"Copy-pasted Druid JS examples that transform values but forget to build the final object; scripts returning JSON.stringify(...) output; scripts returning null on unmatched input.","solutions":["Change the script so it returns an object literal: return { key: value, ... };","If the script computes a single value, wrap it: return { value: computed };","Handle unmatched input by returning an empty object or a sentinel key rather than null.","Remove JavaScript usage in favor of native extraction transforms where possible (JS is disabled by default in many deployments)."],"exampleFix":"// before\n\"function(str) { return str.split(',')[0]; }\"\n// after\n\"function(str) { return { value: str.split(',')[0] }; }\"","handlingStrategy":"validation","validationCode":"// Validate the JS function's return shape with a test input before deployment:\nObject out = fn.apply(\"sample-input\");\nif (!(out instanceof Map)) {\n  throw new IllegalStateException(\"JS function must return a {key: value} object, got: \" + out);\n}","typeGuard":"static boolean isJsParserResultValid(final Object compiled) {\n  return compiled instanceof Map;\n}","tryCatchPattern":"try {\n  return jsParser.parseToMap(input);\n} catch (ParseException e) {\n  log.warn(e, \"JS parser returned non-map for row: %s\", input);\n  return java.util.Collections.emptyMap();\n}","preventionTips":["Always end the JS function with 'return { ... };' object literal.","Test the script with representative inputs before wiring it into ingestion.","Return an empty object rather than null for unmatched inputs.","Prefer native Druid transforms/filters over JavaScript where possible (JS often disabled by config)."],"tags":["javascript","parsing","ingestion","return-type"],"backgroundTag":"unexpected-response-shape","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}