{"record":{"id":"b0f9c92b8d82b339","repo":"apache/druid","slug":"if-an-external-table-provides-columns-it-must-als","errorCode":null,"errorMessage":"If an external table provides columns, it must also provide a format","messagePattern":"If an external table provides columns, it must also provide a format","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/catalog/model/table/FormattedInputSourceDefn.java","lineNumber":66,"sourceCode":"{\n  public static final String FORMAT_PARAMETER = \"format\";\n\n  private Map<String, InputFormatDefn> formats;\n\n  @Override\n  public void bind(TableDefnRegistry registry)\n  {\n    formats = registry.formats();\n    super.bind(registry);\n  }\n\n  @Override\n  public void validate(ResolvedExternalTable table)\n  {\n    final boolean hasColumns = !CollectionUtils.isNullOrEmpty(table.resolvedTable().spec().columns());\n    final boolean hasFormat = table.inputFormatMap != null;\n    if (hasColumns && !hasFormat) {\n      throw new IAE(\"If an external table provides columns, it must also provide a format\");\n    }\n    if (!hasColumns && hasFormat) {\n      throw new IAE(\"If an external table provides a format, it must also provide columns\");\n    }\n    super.validate(table);\n  }\n\n  @Override\n  protected AdHocTableFunction defineAdHocTableFunction()\n  {\n    List<ParameterDefn> fullTableParams = adHocTableFnParameters();\n    List<ParameterDefn> allParams = addFormatParameters(fullTableParams);\n    return new AdHocTableFunction(allParams);\n  }\n\n  /**\n   * Overridden by subclasses to provide the list of table function parameters for\n   * this specific input format. This list is combined with parameters for input","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/catalog/model/table/FormattedInputSourceDefn.java#L48-L84","documentation":"FormattedInputSourceDefn.validate requires that if an external table declares columns, it must also declare an input format. Columns cannot be interpreted without knowing the format of the underlying data, so a columns-without-format spec is invalid.","triggerScenarios":"Validating an external table where spec().columns() is non-empty but inputFormatMap is null; e.g. saving a formatted external table spec with a schema but no format property.","commonSituations":"Manually editing table JSON and deleting the format block while keeping columns; API clients that send partial updates dropping the format; templates that set columns but leave format unset.","solutions":["Add the format property (e.g. {\"type\":\"csv\",...}) to the input format map.","Remove the column definitions if the format should be auto-detected instead.","Provide both columns and format together in the spec.","Re-save the table through the UI/console so both fields are populated."],"exampleFix":"// before\n{\"columns\":[{\"name\":\"a\",\"type\":\"bigint\"}]}\n// after\n{\"columns\":[{\"name\":\"a\",\"type\":\"bigint\"}],\"inputFormat\":{\"type\":\"csv\"}}","handlingStrategy":"validation","validationCode":"boolean hasCols = spec.getColumns() != null && !spec.getColumns().isEmpty();\nboolean hasFmt = spec.getInputFormat() != null;\nif (hasCols && !hasFmt) throw new IllegalArgumentException(\"columns require an inputFormat\");","typeGuard":null,"tryCatchPattern":"try { saveExternalTable(spec); } catch (IAE e) { if (e.getMessage().contains(\"must also provide a format\")) { spec.setInputFormat(defaultFormatFor(spec)); saveExternalTable(spec); } }","preventionTips":["Always ship columns and format together in external table specs","Validate the pair locally before API submission","Re-check specs after partial updates that may drop format","Use the console wizard which populates both fields"],"tags":["druid","external-table","format","validation"],"backgroundTag":"schema-validation-failed","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"}