{"record":{"id":"19bf6df991904e06","repo":"signalapp/Signal-Server","slug":"could-not-interpret-bytes-as-a-zk-credential-publi","errorCode":null,"errorMessage":"Could not interpret bytes as a ZK credential public key","messagePattern":"Could not interpret bytes as a ZK credential public key","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/util/ZkCredentialPublicKeyAdapter.java","lineNumber":52,"sourceCode":"    @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":34,"sourceCodeEnd":57,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/util/ZkCredentialPublicKeyAdapter.java#L34-L57","documentation":"Thrown by ZkCredentialPublicKeyAdapter.deserialize when the base64 decoding succeeded but the resulting bytes cannot construct a ZkCredentialPublicKey (libsignal InvalidInputException). The adapter notes this should essentially never happen since ZkCredentialPublicKey is a plain ByteArray wrapper, so hitting it means the payload is genuinely not the expected key type or the byte length/format is wrong.","triggerScenarios":"Deserializing a JSON field whose value base64-decodes to bytes that ZkCredentialPublicKey's constructor rejects under InvalidInputException — e.g. zero-length after a path change, or bytes produced by a different signal protocol version with a different key encoding.","commonSituations":"Version mismatch between client and server signal-protocol libraries; a field holding the wrong key (e.g. a different credential or an identity key) pasted into the ZK credential public key slot; corrupted binary payloads re-encoded to base64.","solutions":["Verify the bytes are actually a ZK credential public key serialized by the same signal-protocol version the server uses.","Align client and server org.whispersystems signal-protocol library versions.","Re-serialize the key from its source object (ZkCredentialPublicKey.serialize()) rather than hand-assembling bytes.","Check the decoded byte length matches the expected key size for your protocol version.","Since this is defensive against an 'impossible' case, catch JsonParseException and reject the payload as invalid rather than retrying."],"exampleFix":"// before (arbitrary bytes)\nbyte[] bad = \"some random bytes\".getBytes();\n// after (serialize from the real key object)\nbyte[] ok = zkCredentialPublicKey.serialize();","handlingStrategy":"try-catch","validationCode":"// before deserializing\nbyte[] bytes = Base64.getDecoder().decode(keyString);\nif (bytes.length == 0) {\n    throw new IllegalArgumentException(\"zk credential public key is empty\");\n}\n// optionally attempt construction early to fail with a clearer error\nnew ZkCredentialPublicKey(bytes);","typeGuard":null,"tryCatchPattern":"try {\n    ZkCredentialPublicKeys keys = objectMapper.readValue(json, ZkCredentialPublicKeys.class);\n} catch (JsonParseException e) {\n    if (e.getCause() instanceof InvalidInputException) {\n        throw new BadRequestException(\"zk credential public key bytes are invalid for this protocol version\");\n    }\n    throw e;\n}","preventionTips":["Serialize keys only via ZkCredentialPublicKey.serialize(), never hand-built byte arrays","Pin matching org.whispersystems signal-protocol versions on client and server","Confirm the field actually holds a ZK credential public key, not another key type","Reject empty and zero-length key strings before they reach the adapter"],"tags":["json","deserialization","zk-credentials","signal"],"backgroundTag":"invalid-argument-format","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"}