{"id":"b20c3488273c3d99","repo":"mongodb/node-mongodb-native","slug":"input-cluster-time-must-have-a-valid-signature-p","errorCode":null,"errorMessage":"input cluster time must have a valid \"signature\" property with BSON Binary hash and BSON Long keyId","messagePattern":"input cluster time must have a valid \"signature\" property with BSON Binary hash and BSON Long keyId","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/sessions.ts","lineNumber":336,"sourceCode":"   * @param clusterTime - the $clusterTime returned by the server from another session in the form of a document containing the `BSON.Timestamp` clusterTime and signature\n   */\n  advanceClusterTime(clusterTime: ClusterTime): void {\n    if (!clusterTime || typeof clusterTime !== 'object') {\n      throw new MongoInvalidArgumentError('input cluster time must be an object');\n    }\n    if (!clusterTime.clusterTime || clusterTime.clusterTime._bsontype !== 'Timestamp') {\n      throw new MongoInvalidArgumentError(\n        'input cluster time \"clusterTime\" property must be a valid BSON Timestamp'\n      );\n    }\n    if (\n      !clusterTime.signature ||\n      clusterTime.signature.hash?._bsontype !== 'Binary' ||\n      (typeof clusterTime.signature.keyId !== 'bigint' &&\n        typeof clusterTime.signature.keyId !== 'number' &&\n        clusterTime.signature.keyId?._bsontype !== 'Long') // apparently we decode the key to number?\n    ) {\n      throw new MongoInvalidArgumentError(\n        'input cluster time must have a valid \"signature\" property with BSON Binary hash and BSON Long keyId'\n      );\n    }\n\n    _advanceClusterTime(this, clusterTime);\n  }\n\n  /**\n   * Used to determine if this session equals another\n   *\n   * @param session - The session to compare to\n   */\n  equals(session: ClientSession): boolean {\n    if (!(session instanceof ClientSession)) {\n      return false;\n    }\n\n    if (this.id == null || session.id == null) {","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/sessions.ts#L318-L354","documentation":"Thrown by ClientSession.advanceClusterTime() when the supplied clusterTime object lacks a well-formed 'signature' property: the signature.hash must be a BSON Binary and signature.keyId must be a bigint, number, or BSON Long. The driver validates this because cluster times are gossiped between sharded/mongos nodes and an invalid signature would corrupt the $clusterTime sent on subsequent commands. It is a MongoInvalidArgumentError raised at the API boundary of advanceClusterTime.","triggerScenarios":"Calling session.advanceClusterTime(ct) where ct was built from JSON.parse (so _bsontype is gone), or hand-constructing a clusterTime object without rehydrating the hash into a BSON Binary and keyId into a BSON Long. Also reachable via session.clusterTime assignment paths that funnel through advanceClusterTime.","commonSituations":"Cross-client cluster-time gossip in sharded setups; deserializing a clusterTime received over a custom transport (HTTP/message queue) without BSON round-tripping; test fixtures that hardcode plain JS objects shaped like clusterTime.","solutions":["Rehydrate the signature before calling advanceClusterTime: set signature.hash to new Binary(buffer, subtype) and signature.keyId to Long.fromString(String(keyId)) from the bson package.","If the clusterTime came from another client's session.clusterTime getter, BSON-serialize then BSON-deserialize it instead of JSON round-tripping, so _bsontype markers survive.","If you do not actually need to advance the cluster time manually, stop calling advanceClusterTime and let the driver manage it via command responses.","Unit-test the clusterTime shape with a type guard (see defense) before passing it in."],"exampleFix":"// before\nconst ct = JSON.parse(jsonClusterTime);\nsession.advanceClusterTime(ct); // throws: signature.hash/_bsontype missing\n\n// after\nimport { Binary, Long } from 'bson';\nconst ct = JSON.parse(jsonClusterTime);\nct.signature = {\n  hash: new Binary(Buffer.from(ct.signature.hash.data), ct.signature.hash.subtype ?? 0),\n  keyId: Long.fromString(String(ct.signature.keyId))\n};\nsession.advanceClusterTime(ct);","handlingStrategy":"validation","validationCode":"function isValidClusterTime(ct: any): boolean {\n  return (\n    ct != null && typeof ct === 'object' &&\n    ct.clusterTime?._bsontype === 'Timestamp' &&\n    ct.signature?.hash?._bsontype === 'Binary' &&\n    (typeof ct.signature?.keyId === 'bigint' ||\n     typeof ct.signature?.keyId === 'number' ||\n     ct.signature?.keyId?._bsontype === 'Long')\n  );\n}\nif (isValidClusterTime(ct)) session.advanceClusterTime(ct);","typeGuard":"import type { ClusterTime } from 'mongodb';\nfunction isClusterTime(v: unknown): v is ClusterTime {\n  return (\n    v != null && typeof v === 'object' &&\n    (v as any).clusterTime?._bsontype === 'Timestamp' &&\n    (v as any).signature?.hash?._bsontype === 'Binary'\n  );\n}","tryCatchPattern":"try {\n  session.advanceClusterTime(ct);\n} catch (e) {\n  if (e instanceof MongoInvalidArgumentError) {\n    // rehydrate BSON types or skip advancing\n  } else throw e;\n}","preventionTips":["BSON-serialize/deserialize cluster times instead of JSON round-tripping to preserve _bsontype.","Prefer letting the driver manage clusterTime via command responses rather than advancing manually.","Wrap any cluster-time gossip layer with the type guard above."],"tags":["sessions","bson","sharding","cluster-time","invalid-argument"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}