{"record":{"id":"b27fd56f49853021","repo":"signalapp/Signal-Server","slug":"could-not-parse-key-as-a-base64-encoded-value","errorCode":null,"errorMessage":"Could not parse key as a base64-encoded value","messagePattern":"Could not parse key as a base64-encoded value","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/util/ZkCredentialPublicKeyAdapter.java","lineNumber":41,"sourceCode":"    @Override\n    public void serialize(final ZkCredentialPublicKey zkCredentialPublicKey,\n        final JsonGenerator jsonGenerator,\n        final SerializerProvider serializers) throws IOException {\n\n      jsonGenerator.writeString(Base64.getEncoder().encodeToString(zkCredentialPublicKey.serialize()));\n    }\n  }\n\n  public static class Deserializer extends JsonDeserializer<ZkCredentialPublicKey> {\n\n    @Override\n    public ZkCredentialPublicKey deserialize(final JsonParser parser, final DeserializationContext context) throws IOException {\n      final byte[] zkCredentialPublicKeyBytes;\n\n      try {\n        zkCredentialPublicKeyBytes = Base64.getDecoder().decode(parser.getValueAsString());\n      } catch (final IllegalArgumentException e) {\n        throw new JsonParseException(parser, \"Could not parse key as a base64-encoded value\", e);\n      }\n\n      if (zkCredentialPublicKeyBytes.length == 0) {\n        return null;\n      }\n\n      try {\n        return new ZkCredentialPublicKey(zkCredentialPublicKeyBytes);\n      } catch (final InvalidInputException e) {\n        // this should really never happen, as ZkCredentialPublicKey simply extends ByteArray\n        throw new JsonParseException(parser, \"Could not interpret bytes as a ZK credential public key\", e);\n      }\n    }\n  }\n}\n","sourceCodeStart":23,"sourceCodeEnd":57,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/util/ZkCredentialPublicKeyAdapter.java#L23-L57","documentation":"Thrown by ZkCredentialPublicKeyAdapter.deserialize when the JSON string value cannot be base64-decoded. The adapter expects the ZK credential public key serialized as a base64 string; invalid base64 (bad characters, wrong padding, empty-ish malformed input) is surfaced as a JsonParseException. This validates encoding before the key bytes are ever interpreted.","triggerScenarios":"Deserializing JSON where the zk credential public key field is a string containing non-base64 characters (spaces, URL-safe '-'/'_' instead of standard '+'/'/'), missing padding, or other characters rejected by java.util.Base64.getDecoder().","commonSituations":"Clients using base64url encoding while the server expects standard base64; manually copy-pasted keys with whitespace or quotes mangled; JSON produced by a different serializer that escapes or wraps the key differently; config files with hand-typed keys.","solutions":["Re-encode the key with standard base64 (java.util.Base64.getEncoder()) on the client/producer side.","Check for base64url vs standard base64 and convert ('-'→'+', '_'→'/', re-pad with '=').","Strip whitespace/newlines from the string before sending.","Verify the key was not truncated or altered by intermediate JSON tooling.","Catch JsonParseException on deserialize and return a 400 identifying the malformed field."],"exampleFix":"// before (base64url, rejected by Base64.getDecoder())\nString key = \"aBcD-eFgH_iJkL\";\n// after (standard base64 with correct alphabet/padding)\nString key = Base64.getEncoder().encodeToString(zkCredentialPublicKey.serialize());","handlingStrategy":"validation","validationCode":"// before sending / deserializing\nString s = rawKeyString.replaceAll(\"\\\\s\", \"\");\nif (!s.matches(\"[A-Za-z0-9+/]*={0,2}\")) {\n    throw new IllegalArgumentException(\"Key is not valid standard base64: \" + s);\n}\nbyte[] bytes = Base64.getDecoder().decode(s);","typeGuard":null,"tryCatchPattern":"try {\n    ZkCredentialPublicKeys keys = objectMapper.readValue(json, ZkCredentialPublicKeys.class);\n} catch (JsonParseException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"base64\")) {\n        throw new BadRequestException(\"zk credential public key must be standard base64\");\n    }\n    throw e;\n}","preventionTips":["Always produce the string via Base64.getEncoder().encodeToString(key.serialize())","Standardize on standard base64 (not base64url) across clients and server","Strip whitespace/newlines before encoding; never hand-copy keys into configs","Add round-trip tests: encode -> decode -> reconstruct key"],"tags":["json","base64","deserialization","zk-credentials"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}