{"record":{"id":"b9e5372eb2151d39","repo":"bazelbuild/bazel","slug":"variable-definitions-must-not-contain-empty-string","errorCode":null,"errorMessage":"Variable definitions must not contain empty strings or leading / trailing commas","messagePattern":"Variable definitions must not contain empty strings or leading / trailing commas","errorType":"validation","errorClass":"OptionsParsingException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/google/devtools/common/options/Converters.java","lineNumber":588,"sourceCode":"\n    @Override\n    public Map.Entry<K, List<V>> convert(String input, @Nullable Object conversionContext)\n        throws OptionsParsingException {\n      int pos = input.indexOf(\"=\");\n      if (allowEmptyKeys == AllowEmptyKeys.NO && pos <= 0) {\n        throw new OptionsParsingException(\n            \"Must be in the form of a 'key=value[,value]' assignment\");\n      }\n\n      String key = pos <= 0 ? \"\" : input.substring(0, pos);\n      List<String> values = SPLITTER.splitToList(input.substring(pos + 1));\n      if (values.contains(\"\")) {\n        // If the list contains exactly the empty string, it means an empty value was passed and we\n        // should instead return an empty list.\n        if (values.size() == 1) {\n          values = ImmutableList.of();\n        } else {\n          throw new OptionsParsingException(\n              \"Variable definitions must not contain empty strings or leading / trailing commas\");\n        }\n      }\n      ImmutableList.Builder<V> convertedValues = ImmutableList.builder();\n      for (String value : values) {\n        convertedValues.add(valueConverter.convert(value, conversionContext));\n      }\n      return Maps.immutableEntry(\n          keyConverter.convert(key, conversionContext), convertedValues.build());\n    }\n  }\n\n  /**\n   * A converter for variable assignments from the parameter list of a blaze command invocation.\n   * Assignments are expected to have the form {@code [name=]value1[,..,valueN]}, where names and\n   * values are defined to be as permissive as possible. If no name is provided, \"\" is used.\n   */\n  public static class StringToStringListConverter","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/com/google/devtools/common/options/Converters.java#L570-L606","documentation":"Thrown by AssignmentToListOfValuesConverter when the comma-separated value list contains an empty string in a position other than being the sole element (a lone empty value is treated as an empty list). This happens when the input has adjacent commas (e.g. 'a,,b'), a trailing comma before '=' splitting ('a=b,'), or a leading comma ('a=,b').","triggerScenarios":"Input whose value side splits on ',' into a list containing \"\" plus other elements: key=,v / key=v, / key=v,,w passed to any flag using AssignmentToListOfValuesConverter. Note pos + 1 substring: if input ends right after '=', values == [\"\"] which is allowed (empty list).","commonSituations":"Trailing commas from joining script arrays (String.join(\",\", parts) with a trailing empty element), copy-paste edits leaving double commas, environment variables that expand to empty inside a comma list, config generators concatenating optional segments without filtering blanks.","solutions":["Remove leading/trailing/double commas from the value list: --flag=key=v1,v2 not --flag=key=v1,,v2 or --flag=key=v1,.","In generating code, filter out empty segments before joining: parts.stream().filter(p -> !p.isEmpty()).collect(joining(\",\")).","To pass an explicitly empty list, use key= alone (a single empty value maps to an empty list) rather than key=, ."],"exampleFix":"# before\n--per_file_copt=//foo/.*=,-O2\n\n# after\n--per_file_copt=//foo/.*=-O2","handlingStrategy":"validation","validationCode":"// Reject empty segments before the parser does\nboolean hasNoEmptySegments(String s) {\n  int pos = s.indexOf(\"=\");\n  if (pos < 0) return false;\n  for (String part : s.substring(pos + 1).split(\",\", -1)) {\n    if (part.isEmpty() && !s.substring(pos + 1).isEmpty()) return false;\n  }\n  return true;\n}","typeGuard":null,"tryCatchPattern":"Catch OptionsParsingException and include the raw flag value in logs; the parser error does not echo the input, so caller-side logging is essential.","preventionTips":["Filter empty strings before joining lists: parts.stream().filter(p -> !p.isEmpty()).collect(joining(\",\"))","Never append a trailing comma 'for later'","Use key= alone to express an empty list"],"tags":["options-parsing","converter","list-validation","bazel"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}