{"record":{"id":"b2bed8b38ac61974","repo":"apache/iceberg","slug":"cannot-parse-reference-requires-string-or-object","errorCode":null,"errorMessage":"Cannot parse reference (requires string or object): \" + node","messagePattern":"Cannot parse reference \\(requires string or object\\): \" \\+ node","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/expressions/ExpressionParser.java","lineNumber":422,"sourceCode":"  private static <T> UnboundTerm<T> term(JsonNode node) {\n    if (node.isTextual()) {\n      return Expressions.ref(node.asText());\n    } else if (node.isObject()) {\n      String type = JsonUtil.getString(TYPE, node);\n      switch (type) {\n        case REFERENCE:\n          return Expressions.ref(JsonUtil.getString(TERM, node));\n        case TRANSFORM:\n          UnboundTerm<T> child = term(JsonUtil.get(TERM, node));\n          String transform = JsonUtil.getString(TRANSFORM, node);\n          return (UnboundTerm<T>)\n              Expressions.transform(child.ref().name(), Transforms.fromString(transform));\n        default:\n          throw new IllegalArgumentException(\"Cannot parse type as a reference: \" + type);\n      }\n    }\n\n    throw new IllegalArgumentException(\n        \"Cannot parse reference (requires string or object): \" + node);\n  }\n}\n","sourceCodeStart":404,"sourceCodeEnd":426,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/expressions/ExpressionParser.java#L404-L426","documentation":"ExpressionParser.term(JsonNode) expects a term node to be either a plain string (a column reference name) or an object with a recognized \"type\". Any other JSON shape (number, array, null, boolean) reaches this final IllegalArgumentException. The term portion of the predicate JSON is structurally malformed.","triggerScenarios":"ExpressionParser.fromJson on predicate JSON where \"term\" is not a string or object — e.g. a number, array, or missing/null node.","commonSituations":"Hand-written expression JSON; tooling that emits the wrong structure for terms; truncation/corruption of stored metadata.","solutions":["Make the term a column-name string or a valid term object in the JSON.","Validate the JSON structure before parsing (term must be textual or object).","Regenerate the expression JSON from the source expression with ExpressionParser.toJson."],"exampleFix":"// before\n// {\"type\":\"predicate\",\"op\":\"eq\",\"term\":[\"col\"],\"values\":[1]}\nExpression e = ExpressionParser.fromJson(node);\n// after\n// {\"type\":\"predicate\",\"op\":\"eq\",\"term\":\"col\",\"values\":[1]}\nExpression e = ExpressionParser.fromJson(node);","handlingStrategy":"type-guard","validationCode":"JsonNode t = predNode.get(\"term\");\nif (t == null || !(t.isTextual() || t.isObject())) {\n  throw new IllegalArgumentException(\"term must be a string or object\");\n}","typeGuard":"boolean isWellFormedTerm(JsonNode n) { return n != null && (n.isTextual() || n.isObject()); }","tryCatchPattern":"try {\n  Expression e = ExpressionParser.fromJson(node);\n} catch (IllegalArgumentException e) {\n  throw new IllegalArgumentException(\"predicate term node is malformed\", e);\n}","preventionTips":["Always emit \"term\" as a column-name string or a typed object.","Validate predicate JSON structure before calling fromJson.","Use toJson output as the canonical template for predicate JSON."],"tags":["expressions","json","deserialization","malformed-input"],"backgroundTag":"invalid-argument-format","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}