in3rsha/sha256-animation · error
Invalid input to hash256.rb. Expecting even number of hex ch
Error message
Invalid input to hash256.rb. Expecting even number of hex characters (i.e. bytes).
What it means
hash256.rb is the Bitcoin double-SHA-256 runner built on sha256lib.rb; it insists input be whole bytes written in hex. It first prepends 0x to any argument lacking it, then requires the total string size to be even — since the prefix is 2 characters, this is exactly the rule that the hex digit count is even (2 digits per byte). An odd digit count would leave half a byte, so the script prints this message and exits before any hashing starts.
Source
Thrown at hash256.rb:18
require_relative "sha256lib.rb"
# ----------
# hash256.rb - The hash function used in Bitcoin. Basically just runs sha256.rb twice.
# ----------
# Command Line Arguments
$input = ARGV[0] || "0x0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c" # "string"|"0xaabbcc"|"0b10110100"
$delay = ARGV[1] || "fast" # [enter|normal|fast|nodelay]
# Assume that every input is hexadecimal bytes
if $input[0..1] != "0x"
$input = "0x" + $input # prepend 0x if there isn't one (so the upcoming functions can detect it correctly)
end
# Make sure it's a valid size (even number of characters)
if $input.size % 2 != 0
puts "Invalid input to hash256.rb. Expecting even number of hex characters (i.e. bytes)."
exit
end
# Convert hex to bytes
$type = input_type($input)
$bytes = bytes($input, $type)
$message = $bytes.map { |x| x.to_s(2).rjust(8, "0") }.join
# Note about hitting enter to step
if $delay == "enter"
puts "Hit enter to step through."
STDIN.gets
end
# Catch Ctrl-C to prevent unsightly errors when terminating early
Signal.trap("SIGINT") do
exit
endView on GitHub (pinned to 871e976d69)
Solutions
- Pad with a leading zero until the hex digit count is even — every byte is exactly 2 digits (0xabc becomes 0x0abc)
- Recount a pasted block header: 80 bytes means 160 hex digits; an odd count means a dropped or added character
- Format every concatenated field with %02x (or rjust(2, '0')) so no field ever contributes an odd digit
- Pre-validate in the caller: input.delete_prefix('0x').size.even? before invoking hash256.rb
Example fix
# before ruby hash256.rb 0xabc # -> Invalid input to hash256.rb. Expecting even number of hex characters (i.e. bytes). # after ruby hash256.rb 0x0abc # 2 whole bytes, hashes fine
Defensive patterns
Strategy: validation
Validate before calling
hex = input.to_s.chomp.delete_prefix('0x')
raise ArgumentError, 'hex digit count must be even (whole bytes), got ' + hex.size.to_s unless hex.size.even?
system('ruby', 'hash256.rb', '0x' + hex) Type guard
def whole_byte_hex?(s)
digits = s.to_s.chomp.delete_prefix('0x')
digits.match(/[^0-9a-fA-F]/).nil? && digits.size.even?
end Try / catch
begin ARGV.replace([hex_arg, 'fast']) load 'hash256.rb' # prints the message, then exits rescue SystemExit warn 'hash256.rb rejected the input (odd number of hex digits)' end
Prevention
- Format every hex byte as exactly 2 digits with %02x when assembling inputs
- Sanity-check known lengths: a Bitcoin block header is 80 bytes = 160 hex digits
- Check digit-count parity in the caller before spawning hash256.rb
- chomp pasted and shell-provided strings — a trailing newline changes the parity the size check sees
When it happens
Trigger: ruby hash256.rb 0xabc (3 hex digits, total size 5, odd); ruby hash256.rb abc (0x gets prepended first, then 0xabc fails the same way); any 80-byte Bitcoin block header where a paste dropped exactly one of its 160 hex characters; a value like 0x05 trimmed down to 0x5.
Common situations: Hand-copying a block-header hex string and losing one character; building headers by concatenating fields (version, previous block, merkle root, time, bits, nonce) where one field was not zero-padded to an even digit count; shell variables carrying a trailing newline that flips the parity of the character count.
Related errors
- Invalid hex string: #{input}
- Invalid binary string: #{input}
- (bitstring is not an exact amount of bytes)
- We only operate on 32-bit words in SHA-256. Your x is #{ARGV
AI-assisted analysis of in3rsha/sha256-animation@871e976d69 (2026-08-23).
Data as JSON: /api/errors/c47a7c9b1fb04f48.
Report an issue: GitHub.