{"record":{"id":"60695bdcf8768ab7","repo":"apolloconfig/apollo","slug":"line-key-value-must-separate-by","errorCode":null,"errorMessage":"line:{} key value must separate by '='","messagePattern":"line:(.+?) key value must separate by '='","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/txtresolver/PropertyResolver.java","lineNumber":132,"sourceCode":"    deleteCommentAndBlankItem(baseCommentItems, baseBlankItems, changeSets);\n    deleteNormalKVItem(oldKeyMapItem, changeSets);\n\n    return changeSets;\n  }\n\n  private boolean isHasRepeatKey(String[] newItems, @NotNull Set<String> repeatKeys) {\n    Set<String> keys = new HashSet<>();\n    int lineCounter = 1;\n    for (String item : newItems) {\n      if (!isCommentItem(item) && !isBlankItem(item)) {\n        String[] kv = parseKeyValueFromItem(item);\n        if (kv != null) {\n          String key = kv[0].toLowerCase();\n          if (!keys.add(key)) {\n            repeatKeys.add(key);\n          }\n        } else {\n          throw new BadRequestException(\"line:\" + lineCounter + \" key value must separate by '='\");\n        }\n      }\n      lineCounter++;\n    }\n    return !repeatKeys.isEmpty();\n  }\n\n  private String[] parseKeyValueFromItem(String item) {\n    int kvSeparator = item.indexOf(KV_SEPARATOR);\n    if (kvSeparator == -1) {\n      return null;\n    }\n\n    String[] kv = new String[2];\n    kv[0] = item.substring(0, kvSeparator).trim();\n    kv[1] = item.substring(kvSeparator + 1).trim();\n    return kv;\n  }","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/apolloconfig/apollo/blob/d95fc18d112589efc09ddcbe1507047584d55251/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/txtresolver/PropertyResolver.java#L114-L150","documentation":"BadRequestException (HTTP 400) from isHasRepeatKey. While scanning lines for duplicate keys, parseKeyValueFromItem returns null when a non-comment, non-blank line contains no '=' character, and the resolver immediately rejects it, naming the 1-based line number. This guard runs before the change-set is built.","triggerScenarios":"Saving namespace config text where a line is neither a comment (#), nor blank, nor a valid 'key=value' pair (e.g. a bare word, a line using ':' as separator, or a value with no key).","commonSituations":"Pasting YAML/INI style 'key: value' into a properties namespace; a stray word or footnote without '='; a line whose only '=' is inside a comment that was not prefixed with #.","solutions":["Edit line N (the number in the message) so it is 'key=value'.","If the line is documentation, prefix it with '#' so it is treated as a comment.","If the line is empty, leave it truly empty (whitespace only) so isBlankItem matches.","Replace ':' separators with '='; Apollo properties do not accept the key:value form here."],"exampleFix":"// before (line 3 has no '=')\napp.name=demo\napp.env=prod\nthis is a note\n\n// after\napp.name=demo\napp.env=prod\n# this is a note","handlingStrategy":"validation","validationCode":"// Surface every line missing '=' BEFORE submit (1-based line numbers, comments/blanks ignored).\nList<Integer> linesMissingEquals(String configText) {\n  List<Integer> bad = new ArrayList<>();\n  String[] lines = configText.split(\"\\\\r?\\\\n\", -1);\n  for (int i = 0; i < lines.length; i++) {\n    String line = lines[i].trim();\n    if (line.isEmpty() || line.startsWith(\"#\")) continue;\n    if (line.indexOf('=') < 0) bad.add(i + 1);\n  }\n  return bad;\n}\n// assert linesMissingEquals(text).isEmpty()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use '=' as the key/value separator everywhere; Apollo properties reject ':'.","Prefix notes/comments with '#'.","Validate the whole block client-side, not line by line after each failure.","Watch for pasted INI/YAML content that uses ':'."],"tags":["config","validation","properties","namespace","bad-request"],"backgroundTag":null,"analyzedSha":"d95fc18d112589efc09ddcbe1507047584d55251","analyzedAt":"2026-08-14T04:00:05.477Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}