{"record":{"id":"91f2e6c33d40e980","repo":"apache/druid","slug":"either-uri-xor-uriprefix-required","errorCode":null,"errorMessage":"Either uri xor uriPrefix required","messagePattern":"Either uri xor uriPrefix required","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions-core/lookups-cached-global/src/main/java/org/apache/druid/query/lookup/namespace/UriExtractionNamespace.java","lineNumber":107,"sourceCode":"      @JsonProperty(value = \"uriPrefix\", required = false)\n          URI uriPrefix,\n      @JsonProperty(value = \"fileRegex\", required = false)\n          String fileRegex,\n      @JsonProperty(value = \"namespaceParseSpec\", required = true)\n          FlatDataParser namespaceParseSpec,\n      @Min(0) @JsonProperty(value = \"pollPeriod\", required = false) @Nullable\n          Period pollPeriod,\n      @Deprecated\n      @JsonProperty(value = \"versionRegex\", required = false)\n          String versionRegex,\n      @JsonProperty(value = \"maxHeapPercentage\") @Nullable\n          Long maxHeapPercentage\n  )\n  {\n    this.uri = uri;\n    this.uriPrefix = uriPrefix;\n    if ((uri != null) == (uriPrefix != null)) {\n      throw new IAE(\"Either uri xor uriPrefix required\");\n    }\n    this.namespaceParseSpec = Preconditions.checkNotNull(namespaceParseSpec, \"namespaceParseSpec\");\n    if (pollPeriod == null) {\n      // Warning because if UriExtractionNamespace is being used for lookups, any updates to the database will not\n      // be picked up after the node starts. So for use casses where nodes start at different times (like streaming\n      // ingestion with peons) there can be data inconsistencies across the cluster.\n      LOG.warn(\"No pollPeriod configured for UriExtractionNamespace - entries will be loaded only once at startup\");\n      this.pollPeriod = Period.ZERO;\n    } else {\n      this.pollPeriod = pollPeriod;\n    }\n    this.fileRegex = fileRegex == null ? versionRegex : fileRegex;\n    if (fileRegex != null && versionRegex != null) {\n      throw new IAE(\"Cannot specify both versionRegex and fileRegex. versionRegex is deprecated\");\n    }\n\n    if (uri != null && this.fileRegex != null) {\n      throw new IAE(\"Cannot define both uri and fileRegex\");","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/lookups-cached-global/src/main/java/org/apache/druid/query/lookup/namespace/UriExtractionNamespace.java#L89-L125","documentation":"UriExtractionNamespace's constructor requires exactly one of `uri` (a single file URI) or `uriPrefix` (a URI prefix for multi-file loading) — the two are mutually exclusive and one is mandatory. When both are set, or neither is set, it throws this IAE during namespace config deserialization.","triggerScenarios":"Constructing UriExtractionNamespace (or submitting lookup JSON) with both uri and uriPrefix set, or with both null/absent; Jackson binding a lookup config missing the uri field.","commonSituations":"Copy-pasted lookup JSON where an old `uri` field was left in place after adding `uriPrefix`; typos in field names (e.g. \"url\" instead of \"uri\") leaving both effectively null; migrated configs with leftover fields.","solutions":["Remove either the `uri` or the `uriPrefix` field so exactly one remains.","If the config should point at a single file, keep `uri` and delete `uriPrefix`; for a directory/prefix of files, keep `uriPrefix` and delete `uri`.","Check for typo'd keys that silently leave both fields null.","Validate the lookup JSON before POSTing it to the lookup coordinator."],"exampleFix":"// before\n{\"type\":\"uri\", \"uri\":\"s3://bucket/f.csv\", \"uriPrefix\":\"s3://bucket/\"}\n// after\n{\"type\":\"uri\", \"uriPrefix\":\"s3://bucket/\"}","handlingStrategy":"validation","validationCode":"// java\nboolean hasUri = cfg.has(\"uri\") && !cfg.get(\"uri\").isNull();\nboolean hasPrefix = cfg.has(\"uriPrefix\") && !cfg.get(\"uriPrefix\").isNull();\nif (hasUri == hasPrefix) throw new IllegalArgumentException(\"exactly one of uri/uriPrefix required\");","typeGuard":null,"tryCatchPattern":"// java\ntry { mapper.readValue(json, UriExtractionNamespace.class); }\ncatch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"Either uri xor uriPrefix\")) { /* fix config */ }\n}","preventionTips":["Keep exactly one of uri/uriPrefix in lookup JSON.","Validate lookup configs against a JSON schema before posting.","When migrating between single-file and prefix setups, delete the other field."],"tags":["lookup","configuration","validation","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-14T05:17:10.506Z"}