in3rsha/sha256-animation · error

Invalid hex string: #{input}

Error message

Invalid hex string: #{input}

What it means

The counterpart of the binary check in input_type() (sha256lib.rb): an argument starting with 0x must have a remainder that fully matches ^[0-9A-F]+$ case-insensitively — at least one character, hex digits only. Anything else, including an empty payload after 0x, prints this message and exits before hashing starts. The guard exists because the next step, bytes(), feeds the payload straight into Ruby pack('H*'), which cannot convert non-hex characters.

Source

Thrown at sha256lib.rb:84

# Detect input type base on prefix (i.e. binary, hex, or otherwise just a string)
def input_type(input)
  # Check if input is referencing a file
  if(File.file?(input))
  	return "file"
  else
	  # Check for hex or binary prefix
	  case input[0..1]
	  when "0b"
		# check it's a valid binary string
		if input[2..-1] =~ /[^0-1]/ # only 1s and 0s
		  puts "Invalid binary string: #{input}"
		  exit
		end
		return "binary"
	  when "0x"
		# check it's a valid hex string
		if input[2..-1] !~ /^[0-9A-F]+$/i # only hex chars (case-insensitive)
		  puts "Invalid hex string: #{input}"
		  exit
		end
		return "hex"
	  else
		return "string"
	  end
  end
end

# Convert input (hex, ascii) to array of bytes
def bytes(input, type)
  case type
  when "binary"
    bin = input[2..-1] # trim 0b prefix
    if (bin.size % 8 == 0) # if we have been given a bitstring that makes up an exact number of bytes (8 bits in a byte)
      bytes = bin.scan(/.{8}/).map {|byte| byte.to_i(2)} # convert the bits to array of bytes (in decimal)
    else
      bytes = "(bitstring is not an exact amount of bytes)" # helpful note

View on GitHub (pinned to 871e976d69)

Solutions

  1. Clean the payload after 0x until it contains only 0-9 and a-f/A-F: remove a duplicated 0x, spaces and punctuation
  2. Give the payload at least one hex digit — a bare '0x' is always rejected
  3. If the text is meant as plain ASCII, drop the 0x prefix so input_type returns 'string'
  4. In your own validation, anchor to the true start and end of the string (not ^ and $) and chomp newlines, so a value like 0xabc plus a trailing newline cannot reach pack('H*') and raise ArgumentError there

Example fix

# before
ruby sha256.rb '0x0xff12'  # -> Invalid hex string: 0x0xff12
ruby sha256.rb '0x'         # -> Invalid hex string: 0x

# after
ruby sha256.rb '0xff12'     # valid hex (case-insensitive)
Defensive patterns

Strategy: validation

Validate before calling

# run before input_type() or the sha256.rb CLI
payload = input.to_s.chomp[2..].to_s
bad_hex = input.to_s.start_with?('0x') && (payload.empty? || payload.match(/[^0-9a-fA-F]/))
abort 'payload after 0x must be non-empty hex digits' if bad_hex
type = input_type(input)

Type guard

def valid_hex_literal?(s)
  text = s.to_s
  payload = text.chomp[2..].to_s
  !text.start_with?('0x') || (!payload.empty? && payload.match(/[^0-9a-fA-F]/).nil?)
end

Try / catch

begin
  type = input_type(candidate)
rescue SystemExit
  warn 'sha256lib rejected the hex input'
end

Prevention

When it happens

Trigger: input_type('0xzz12') or ruby sha256.rb 0xg1 (non-hex letters); input_type('0x') (empty payload fails the one-or-more rule); '0x0xff' (prefix doubled by concatenation); '0x1a 2b' (space). Plain text meant as a string that begins with 0x, for example hashing '0xprotocol' as ASCII, is caught too. Beware a trailing newline: it slips past this regex (the $ anchor matches before a final newline) and instead raises ArgumentError later inside pack('H*').

Common situations: Gluing strings together and doubling the 0x prefix ('0x' + '0xff'); copy-pasting hex that gained a space or was trimmed down to empty; command substitution leaving a trailing newline; intending ASCII text that starts with 0x.

Related errors


AI-assisted analysis of in3rsha/sha256-animation@871e976d69 (2026-08-23). Data as JSON: /api/errors/b36a81f897756465. Report an issue: GitHub.