{"record":{"id":"a1a2b91323b90bb2","repo":"linera-io/linera-protocol","slug":"label-must-be-exactly-n-bytes-hex-chars","errorCode":null,"errorMessage":"{label} must be exactly {N} bytes ({} hex chars)","messagePattern":"(.+?) must be exactly (.+?) bytes \\((.+?) hex chars\\)","errorType":"validation","errorClass":"async_graphql::Error","httpStatus":null,"severity":"error","filePath":"linera-bridge/contracts/evm-bridge/src/service.rs","lineNumber":78,"sourceCode":"                runtime: self.runtime.clone(),\n            },\n            EmptySubscription,\n        )\n        .finish();\n        schema.execute(request).await\n    }\n}\n\n/// Decodes a hex string (optional `0x` prefix) into bytes.\nfn decode_hex(label: &str, s: &str) -> async_graphql::Result<Vec<u8>> {\n    hex::decode(s.strip_prefix(\"0x\").unwrap_or(s))\n        .map_err(|e| async_graphql::Error::new(format!(\"invalid {label} hex: {e}\")))\n}\n\n/// Decodes a hex string into a fixed-size byte array.\nfn decode_hex_array<const N: usize>(label: &str, s: &str) -> async_graphql::Result<[u8; N]> {\n    decode_hex(label, s)?.as_slice().try_into().map_err(|_| {\n        async_graphql::Error::new(format!(\n            \"{label} must be exactly {N} bytes ({} hex chars)\",\n            N * 2\n        ))\n    })\n}\n\n/// GraphQL mutation root: schedules bridge operations submitted by a client (the\n/// demo UI, an admin tool, or a custom relayer). The application bytecode — and\n/// thus this interface — cannot change after deployment, so every\n/// [`BridgeOperation`] is exposed; on-chain authorization still gates each.\n/// Byte-array arguments are hex-encoded strings (with or without `0x`).\npub struct MutationRoot {\n    runtime: Arc<ServiceRuntime<EvmBridgeService>>,\n}\n\n#[Object]\nimpl MutationRoot {\n    /// Burns `amount` wrapped tokens from the operation's authenticated signer","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/contracts/evm-bridge/src/service.rs#L60-L96","documentation":"After decode_hex succeeds, decode_hex_array converts the decoded bytes into a fixed-size [u8; N] for typed fields (32-byte tx hashes or topics, 20-byte addresses, etc.). try_into fails when the decoded length differs from N, so this error means the hex was valid but the wrong length. The message states the exact requirement: N bytes, i.e. N*2 hex characters.","triggerScenarios":"Calling a bridge-service GraphQL field routed through decode_hex_array with a valid-hex string whose length is not 2N hex chars: e.g. a 40-hex-char (20-byte) EVM address where 64 hex chars (32 bytes) are required, a truncated hash, or an over-long value.","commonSituations":"Mixing up field types: passing an Ethereum address (20 bytes) as a transaction hash (32 bytes) or vice versa; using a topic hash without left-padding to 32 bytes; pasting a hash that lost leading zeros when converted through a decimal intermediate; confusing byte count with hex-char count (sending 32 hex chars thinking that is 32 bytes).","solutions":["Count the hex characters (after the optional 0x): it must be exactly 2N — 64 chars for 32-byte hashes/topics, 40 for 20-byte addresses — and match the field named in the message.","If the value is short, left-pad with zeros to the required length; if long, verify you have the right value (e.g. a full keccak hash, not its truncated form).","Double-check you are putting the value in the right argument (address vs hash vs topic) per the field's label in the message."],"exampleFix":"// before\n// 32-byte field, but only 4 hex chars given\ntopic: \"0xabcd\"  // -> \"topic must be exactly 32 bytes (64 hex chars)\"\n\n// after\ntopic: \"0x000000000000000000000000000000000000000000000000000000000000abcd\"  // 64 hex chars","handlingStrategy":"validation","validationCode":"fn has_hex_len(s: &str, n_bytes: usize) -> bool {\n    let body = s.strip_prefix(\"0x\").unwrap_or(s);\n    body.len() == n_bytes * 2 && body.bytes().all(|b| b.is_ascii_hexdigit())\n}\n\n// e.g. has_hex_len(topic, 32) before sending it to the bridge service","typeGuard":"fn is_32_byte_hex(s: &str) -> bool {\n    let body = s.strip_prefix(\"0x\").unwrap_or(s);\n    body.len() == 64 && body.bytes().all(|b| b.is_ascii_hexdigit())\n}","tryCatchPattern":null,"preventionTips":["Keep a per-field expected length table (address=20, txHash/topic=32) and assert before each request.","Left-pad short values to the field width instead of hoping the server accepts them.","Use typed client structs (e.g. alloy/primitive types H256/H160) that make wrong-length values unrepresentable."],"tags":["rust","graphql","hex","byte-length","evm-bridge"],"backgroundTag":"invalid-hex-length","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}