{"record":{"id":"a26a58c9c8f352c6","repo":"pentaho/pentaho-kettle","slug":"key-must-not-end-with","errorCode":null,"errorMessage":"Key must not end with '-'","messagePattern":"Key must not end with '-'","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/core/util/KeyValue.java","lineNumber":96,"sourceCode":"  }\n\n  /**\n   * @param lowerKey\n   *          key to test.\n   * @throws IllegalArgumentException\n   *           if key is invalid.\n   */\n  public static final void assertKey( final String lowerKey ) throws IllegalArgumentException {\n    Assert.assertNotEmpty( lowerKey, \"Key cannot be null or empty\" );\n    if ( !StringUtils.containsOnly( lowerKey, VALID_KEY_CHARS ) ) {\n      throw new IllegalArgumentException( \"Key contains invalid characters [validKeyCharacters=\"\n        + VALID_KEY_CHARS + \"]\" );\n    }\n    if ( lowerKey.charAt( 0 ) == '-' ) {\n      throw new IllegalArgumentException( \"Key must not start with '-'\" );\n    }\n    if ( lowerKey.endsWith( \"-\" ) ) {\n      throw new IllegalArgumentException( \"Key must not end with '-'\" );\n    }\n    if ( \"_\".equals( lowerKey ) ) {\n      throw new IllegalArgumentException( \"Key must not be  '_'\" );\n    }\n  }\n\n  /**\n   * @return the key, never null.\n   */\n  public String getKey() {\n    return this.key;\n  }\n\n  /**\n   * @return the value\n   */\n  public T getValue() {\n    return this.value;","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/core/util/KeyValue.java#L78-L114","documentation":"KeyValue.assertKey() rejects keys that end with '-' because a trailing hyphen is ambiguous or awkward in downstream usages (env vars, flags, concatenation). It throws IllegalArgumentException('Key must not end with \\'-\\'').","triggerScenarios":"Constructing a KeyValue with a key such as 'retries-' — often the result of appending separators during programmatic key building (prefix + '-' with an empty suffix).","commonSituations":"Dynamic key construction where a suffix is optional: \"prefix-\" + suffix with empty suffix; templated keys with placeholder removed leaving a trailing dash.","solutions":["Strip trailing '-' characters before creating the KeyValue.","Fix key-building logic to avoid appending a separator when the suffix is empty.","Normalize keys with a helper that trims leading/trailing hyphens and validates with assertKey."],"exampleFix":"// before\nString key = \"retries-\"; // built as \"retries\" + \"-\" + emptySuffix\nnew KeyValue<>(key, 5);\n// after\nString key = (\"retries\" + \"-\" + suffix).replaceAll(\"^-+|-+$\", \"\");\nnew KeyValue<>(key, 5); // \"retries\"","handlingStrategy":"validation","validationCode":"if (key.endsWith(\"-\")) {\n  throw new IllegalArgumentException(\"Key must not end with '-': \" + key);\n}","typeGuard":null,"tryCatchPattern":"try {\n  new KeyValue<>(key, value);\n} catch (IllegalArgumentException e) {\n  key = key.replaceAll(\"-+$\", \"\");\n}","preventionTips":["Avoid appending separators to empty suffixes when building keys dynamically: suffix.isEmpty() ? prefix : prefix + \"-\" + suffix.","Trim trailing hyphens in the shared key-normalization helper.","Unit-test key builders with empty optional parts."],"tags":["validation","key-format","illegal-argument"],"backgroundTag":"invalid-identifier-format","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}