{"record":{"id":"1357c6fefc107f3a","repo":"openzipkin/zipkin","slug":"key-null-1357c6","errorCode":null,"errorMessage":"key == null","messagePattern":"key == null","errorType":"validation","errorClass":"NullPointerException","httpStatus":400,"severity":"error","filePath":"zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchAutocompleteTags.java","lineNumber":39,"sourceCode":"  final SearchCallFactory search;\n  final int namesLookback;\n  final Call<List<String>> keysCall;\n\n  ElasticsearchAutocompleteTags(ElasticsearchStorage es) {\n    this.search = new SearchCallFactory(es.http());\n    this.indexNameFormatter = es.indexNameFormatter();\n    this.enabled = es.searchEnabled() && !es.autocompleteKeys().isEmpty();\n    this.namesLookback = es.namesLookback();\n    this.keysCall = Call.create(es.autocompleteKeys());\n  }\n\n  @Override public Call<List<String>> getKeys() {\n    if (!enabled) return Call.emptyList();\n    return keysCall.clone();\n  }\n\n  @Override public Call<List<String>> getValues(String key) {\n    if (key == null) throw new NullPointerException(\"key == null\");\n    if (key.isEmpty()) throw new IllegalArgumentException(\"key was empty\");\n    if (!enabled) return Call.emptyList();\n\n    long endMillis = System.currentTimeMillis();\n    long beginMillis = endMillis - namesLookback;\n    List<String> indices =\n      indexNameFormatter.formatTypeAndRange(TYPE_AUTOCOMPLETE, beginMillis, endMillis);\n\n    if (indices.isEmpty()) return Call.emptyList();\n\n    SearchRequest.Filters filters =\n      new SearchRequest.Filters().addTerm(\"tagKey\", key);\n\n    SearchRequest request = SearchRequest.create(indices)\n      .filters(filters)\n      .addAggregation(Aggregation.terms(\"tagValue\", Integer.MAX_VALUE));\n    return search.newCall(request, BodyConverters.KEYS);\n  }","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchAutocompleteTags.java#L21-L57","documentation":"ElasticsearchAutocompleteTags.getValues(key) fetches distinct values for an autocomplete tag key within the names lookback window. The API contract requires a non-null, non-empty key; null fails fast with NullPointerException('key == null') before any query is built. This is a programmer-error guard, not an environmental failure.","triggerScenarios":"Calling autocompleteTags().getValues(null) on ElasticsearchStorage, typically because a caller forwarded an unvalidated request parameter (e.g. a missing ?key= query param from an HTTP API) straight into getValues.","commonSituations":"Building a custom UI or REST facade over the SpanStore API where the key arrives from user input and optional parameters are not filtered; refactoring that introduces a null path; tests that pass null by accident.","solutions":["Fix the caller to pass a non-null key; trace where the null originates (often an unchecked request parameter).","Validate at the boundary: reject or skip requests with a missing key before reaching storage.","If the key is optional in your API, return an empty list instead of calling getValues."],"exampleFix":"// before\nString key = request.getParameter(\"key\"); // may be null\nCall<List<String>> values = storage.autocompleteTags().getValues(key); // NPE\n\n// after\nString key = request.getParameter(\"key\");\nif (key == null || key.isEmpty()) return Collections.emptyList();\nCall<List<String>> values = storage.autocompleteTags().getValues(key);","handlingStrategy":"validation","validationCode":"if (key == null || key.isEmpty()) {\n  return Collections.emptyList(); // or reject the request with 400\n}","typeGuard":"static boolean isQueryableKey(String key) {\n  return key != null && !key.isEmpty();\n}","tryCatchPattern":null,"preventionTips":["Validate request parameters at the HTTP boundary before touching the SpanStore API.","Treat autocomplete keys like required query params: reject with 400, never forward null.","Cover getValues(null) with a unit test asserting the NPE contract if you wrap this API."],"tags":["elasticsearch","autocomplete","null-check","api-contract","storage"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}