{"record":{"id":"4177b9ff57f3c233","repo":"apache/druid","slug":"either-columns-or-findcolumnsfromheader-must-b","errorCode":null,"errorMessage":"Either [columns] or [findColumnsFromHeader] must be set","messagePattern":"Either \\[columns\\] or \\[findColumnsFromHeader\\] must be set","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/data/input/impl/FlatTextInputFormat.java","lineNumber":70,"sourceCode":"      @Nullable List<String> columns,\n      @Nullable String listDelimiter,\n      String delimiter,\n      @Nullable Boolean hasHeaderRow,\n      @Nullable Boolean findColumnsFromHeader,\n      int skipHeaderRows,\n      @Nullable Boolean tryParseNumbers\n  )\n  {\n    this.columns = columns == null ? Collections.emptyList() : columns;\n    this.listDelimiter = listDelimiter;\n    this.delimiter = Preconditions.checkNotNull(delimiter, \"delimiter\");\n    if (columns == null || columns.isEmpty()) {\n      if (hasHeaderRow != null && findColumnsFromHeader != null) {\n        // User provided both hasHeaderRow and findColumnsFromHeader.\n        throw new IAE(\"Cannot accept both [findColumnsFromHeader] and [hasHeaderRow]\");\n      } else if (hasHeaderRow == null && findColumnsFromHeader == null) {\n        // User provided neither columns, nor one of the header-related parameters.\n        throw new IAE(\"Either [columns] or [findColumnsFromHeader] must be set\");\n      } else {\n        // User provided one of hasHeaderRow or findColumnsFromHeader. Take the one they provided.\n        this.findColumnsFromHeader = hasHeaderRow != null ? hasHeaderRow : findColumnsFromHeader;\n      }\n    } else {\n      this.findColumnsFromHeader = findColumnsFromHeader == null ? false : findColumnsFromHeader;\n    }\n    this.skipHeaderRows = skipHeaderRows;\n    Preconditions.checkArgument(\n        !delimiter.equals(listDelimiter),\n        \"Cannot have same delimiter and list delimiter of [%s]\",\n        delimiter\n    );\n    this.tryParseNumbers = tryParseNumbers == null ? false : tryParseNumbers;\n\n    if (!this.columns.isEmpty()) {\n      for (String column : this.columns) {\n        Preconditions.checkArgument(","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/data/input/impl/FlatTextInputFormat.java#L52-L88","documentation":"FlatTextInputFormat requires a way to learn the column names: either an explicit 'columns' list or header detection via 'findColumnsFromHeader'/'hasHeaderRow'. When neither is provided it cannot build a signature and throws IAE in the constructor.","triggerScenarios":"Creating FlatTextInputFormat with columns null or empty while hasHeaderRow and findColumnsFromHeader are both null (e.g. an inputFormat spec with just {\"type\":\"csv\"}).","commonSituations":"Minimal CSV ingestion specs that forgot header configuration; generated specs where the columns field was dropped by a templating step; users assuming the first row is used as a header by default.","solutions":["Add \"findColumnsFromHeader\": true to read column names from the file's first row","Or supply an explicit \"columns\": [...] array in the inputFormat","Or set the legacy \"hasHeaderRow\": true if on an older API surface"],"exampleFix":"// before\n{\"type\":\"csv\"}\n// after\n{\"type\":\"csv\",\"findColumnsFromHeader\":true}","handlingStrategy":"validation","validationCode":"// Before submitting the spec\nMap<String,Object> fmt = (Map<String,Object>) spec.get(\"inputFormat\");\nboolean hasColumns = fmt.get(\"columns\") instanceof List && !((List<?>) fmt.get(\"columns\")).isEmpty();\nboolean hasHeader = fmt.get(\"findColumnsFromHeader\") != null || fmt.get(\"hasHeaderRow\") != null;\nif (!hasColumns && !hasHeader) { throw new IllegalArgumentException(\"csv/tsv inputFormat needs columns or findColumnsFromHeader\"); }","typeGuard":null,"tryCatchPattern":"try { buildFormat(cfg); } catch (IAE e) { cfg.put(\"findColumnsFromHeader\", true); buildFormat(cfg); }","preventionTips":["Never submit a bare {\"type\":\"csv\"} inputFormat","Default templates should always include columns or findColumnsFromHeader","Validate ingestion specs in CI with a dry-run parser"],"tags":["configuration","csv","missing-field"],"backgroundTag":"missing-required-config-field","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}