{"record":{"id":"cfd64a17fd41ea11","repo":"apache/druid","slug":"external-http-tables-must-provide-only-one-of-a-ur","errorCode":null,"errorMessage":"External HTTP tables must provide only one of a URI or a %s property","messagePattern":"External HTTP tables must provide only one of a URI or a (.+?) property","errorType":"validation","errorClass":"IAE","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/catalog/model/table/HttpInputSourceDefn.java","lineNumber":149,"sourceCode":"\n  @Override\n  public void validate(ResolvedExternalTable table)\n  {\n    final Map<String, Object> sourceMap = table.inputSourceMap;\n    final boolean hasUri = sourceMap.containsKey(URIS_FIELD);\n    final String uriTemplate = table.resolvedTable().stringProperty(URI_TEMPLATE_PROPERTY);\n    final boolean hasTemplate = uriTemplate != null;\n    final boolean hasFormat = table.inputFormatMap != null;\n    final boolean hasColumns = !CollectionUtils.isNullOrEmpty(table.resolvedTable().spec().columns());\n\n    if (!hasUri && !hasTemplate) {\n      throw new IAE(\n          \"External HTTP tables must provide either a URI or a %s property\",\n          URI_TEMPLATE_PROPERTY\n      );\n    }\n    if (hasUri && hasTemplate) {\n      throw new IAE(\n          \"External HTTP tables must provide only one of a URI or a %s property\",\n          URI_TEMPLATE_PROPERTY\n      );\n    }\n    if (hasUri && !hasFormat) {\n      throw new IAE(\n          \"An external HTTP table with a URI must also provide the corresponding format\"\n      );\n    }\n    if (hasUri && !hasColumns) {\n      throw new IAE(\n          \"An external HTTP table with a URI must also provide the corresponding columns\"\n      );\n    }\n    if (hasTemplate) {\n\n      // Verify the template\n      templateMatcher(uriTemplate);","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/catalog/model/table/HttpInputSourceDefn.java#L131-L167","documentation":"Thrown by HttpInputSourceDefn.validate when an external HTTP table supplies BOTH a direct URI and a uriTemplate property. The two are mutually exclusive addressing modes: either fixed URIs or a template with per-URI substitution, not both.","triggerScenarios":"A table definition where the input source has 'uris' and the table also carries a uriTemplate property; validate detects hasUri && hasTemplate simultaneously.","commonSituations":"Migrating a table from a fixed-URI definition to a template and forgetting to remove the old uris field; merging table definitions where both styles were combined; a UI or script that writes both fields unconditionally.","solutions":["Remove the uriTemplate property if fixed URIs are intended","Remove the uris field from the input source if template-based addressing is intended","Ensure whichever generation tool writes the table writes only one of the two fields"],"exampleFix":"// before\n{\"type\": \"http\", \"uris\": [\"https://a.com/f.json\"]} + tableProperty uriTemplate=\"https://a.com/{}.json\"\n// after\n{\"type\": \"http\", \"uris\": [\"https://a.com/f.json\"]} // template removed","handlingStrategy":"validation","validationCode":"if (uris != null && uriTemplate != null) { throw new IAE(\"provide only one of uris/uriTemplate\"); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When migrating to templates, delete the uris field","Ensure generators emit exactly one addressing mode","Add a lint rule for table definitions"],"tags":["catalog","http","mutually-exclusive"],"backgroundTag":"mutually-exclusive-options","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"}