apolloconfig/apollo · error · BadRequestException

Invalid encoded key

Error message

Invalid encoded key

What it means

HTTP 400 (BadRequestException). Thrown by ItemController.decodeBase64 when the {key} path parameter supplied to getItem/updateItem/deleteItem cannot be decoded as EITHER standard Base64 or URL-safe Base64 (both java.util.Base64 decoders raise IllegalArgumentException). Apollo OpenAPI requires item keys to be Base64-encoded so keys containing '/', '+', and special characters survive URL transport.

Source

Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java:403

      if (!hasPermission) {
        noPermissionNamespace = namespaceIdentifier;
        break;
      }
    }
    if (!hasPermission) {
      throw new AccessDeniedException(String
          .format("You don't have the permission to modify namespace: %s", noPermissionNamespace));
    }
  }

  private String decodeBase64(String key) {
    try {
      return decodeBase64(key, Base64.getDecoder());
    } catch (IllegalArgumentException standardBase64Exception) {
      try {
        return decodeBase64(key, Base64.getUrlDecoder());
      } catch (IllegalArgumentException urlBase64Exception) {
        throw new BadRequestException("Invalid encoded key");
      }
    }
  }

  private String decodeBase64(String key, Base64.Decoder decoder) {
    return new String(decoder.decode(key), StandardCharsets.UTF_8);
  }

  void doSyntaxCheck(NamespaceTextModel model) {
    NamespaceTextSyntaxChecker.check(model);
  }
}

View on GitHub (pinned to d95fc18d11)

Solutions

  1. Base64-encode the item key before placing it in the URL path using standard Base64 (URL-safe also accepted).
  2. If the key is already Base64 but still failing, ensure your HTTP client is not additionally percent-encoding the Base64 characters (+,/,=) and not double-encoding.
  3. Trim whitespace/newlines that some Base64 encoders append before sending.
  4. Verify the exact encoded string round-trips locally with Base64.getDecoder().decode(...) before the call.

Example fix

// before
String url = "/items/timeout.ms"; // raw key -> 400

// after
String encoded = Base64.getEncoder().encodeToString("timeout.ms".getBytes(StandardCharsets.UTF_8));
String url = "/items/" + encoded;
Defensive patterns

Strategy: validation

Validate before calling

// Validate Base64 round-trip locally before the call.
import java.util.Base64;
String rawKey = "timeout.ms";
String encoded = Base64.getEncoder().encodeToString(rawKey.getBytes(StandardCharsets.UTF_8));
// sanity: decodes with standard OR url decoder
try { Base64.getDecoder().decode(encoded); }
catch (IllegalArgumentException e) { Base64.getUrlDecoder().decode(encoded); } // throws if truly invalid -> fix before sending

Type guard

null

Try / catch

try {
  client.getItem(appId, env, cluster, ns, encodedKey);
} catch (HttpClientErrorException.BadRequest e) {
  if (e.getResponseBodyAsString().contains("Invalid encoded key")) {
    // re-encode the raw key and retry once
    encodedKey = Base64.getEncoder().encodeToString(rawKey.getBytes(StandardCharsets.UTF_8));
  }
}

Prevention

When it happens

Trigger: GET/PUT/DELETE /openapi/v1/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key} where {key} is the raw, un-encoded item key; or a key that was double-encoded, truncated by a URL shortener, or mangled by an HTTP client that URL-encoded the Base64 string a second time.

Common situations: Caller passed the plaintext key 'timeout.ms' directly instead of Base64('timeout.ms'); copy-paste from a browser that percent-encoded '+', '/', '='; a key containing spaces or non-ASCII passed through un-encoded; version mismatch where an older client expected plaintext keys.

Related errors


AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14). Data as JSON: /api/errors/f539cb7e83ce2a00. Report an issue: GitHub.