{"record":{"id":"f8197c87d0ef110b","repo":"apache/druid","slug":"use-a-table-function-to-set-either-s-or-s","errorCode":null,"errorMessage":"Use a table function to set either %s or %s","messagePattern":"Use a table function to set either (.+?) or (.+?)","errorType":"validation","errorClass":"IAE","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/catalog/model/table/LocalInputSourceDefn.java","lineNumber":272,"sourceCode":"    // we list. Take pity on the user and provide a filter in this case.\n    String filter = CatalogUtils.getString(jsonMap, FILTER_FIELD);\n    if (filter != null) {\n      return;\n    }\n    String baseDir = CatalogUtils.getString(jsonMap, BASE_DIR_FIELD);\n    if (baseDir != null) {\n      jsonMap.put(FILTER_FIELD, \"*\");\n    }\n  }\n\n  @Override\n  public ExternalTableSpec convertTable(ResolvedExternalTable table)\n  {\n    final Map<String, Object> sourceMap = new HashMap<>(table.inputSourceMap);\n    final boolean hasFiles = !CollectionUtils.isNullOrEmpty(CatalogUtils.safeGet(sourceMap, FILES_FIELD, List.class));\n    final boolean hasFilter = !Strings.isNullOrEmpty(CatalogUtils.getString(sourceMap, FILTER_FIELD));\n    if (!hasFiles && !hasFilter) {\n      throw new IAE(\n          \"Use a table function to set either %s or %s\",\n          FILES_PARAMETER,\n          FILTER_PARAMETER\n      );\n    }\n    return super.convertTable(table);\n  }\n}\n","sourceCodeStart":254,"sourceCodeEnd":281,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/catalog/model/table/LocalInputSourceDefn.java#L254-L281","documentation":"Druid's LocalInputSource rejects input sources that specify neither files nor filter — a bare baseDir alone cannot be converted to a valid input source (it needs a filter, which the catalog adds only for table-function paths). When converting a completed table whose stored input source has neither files nor filter, convertTable throws this IAE directing the user to the table function.","triggerScenarios":"Querying an external table with type 'local' whose inputSourceMap contains only baseDir (no files, no filter) via a path that bypasses the table function; tables defined with baseDir but never finalized with a table function.","commonSituations":"Defining a table spec with just baseDir and trying to use it directly; programmatic table creation skipping the table-function resolution step; hand-authored catalog JSON.","solutions":["Run the table function once with files or filter to finalize the table definition","Add a filter property (e.g. \"*\") to the table's input source","Add an explicit files list to the table's input source"],"exampleFix":"// before\n{\"type\":\"local\",\"baseDir\":\"/data\"}\n// after\n{\"type\":\"local\",\"baseDir\":\"/data\",\"filter\":\"*\"}","handlingStrategy":"validation","validationCode":"// Java\nMap<String,Object> src = table.inputSourceMap;\nif (!CollectionUtils.isNullOrEmpty((List<?>) src.getOrDefault(\"files\", List.of())) == false\n    && Strings.isNullOrEmpty((String) src.get(\"filter\")))\n  throw new IllegalArgumentException(\"finalize local table via table function or add filter\");","typeGuard":null,"tryCatchPattern":"try { table.convert(); } catch (IAE e) { if (e.getMessage().contains(\"Use a table function\")) { /* invoke the table function with files/filter first */ } else throw e; }","preventionTips":["Never query a local table that defines only baseDir — finalize it first","Add filter \"*\" if all files under baseDir are wanted","Keep table definitions and their intended usage (direct vs function) in sync"],"tags":["druid-catalog","validation","local-input-source","table-definition"],"backgroundTag":"missing-required-argument","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}