in3rsha/sha256-animation · error

(bitstring is not an exact amount of bytes)

Error message

(bitstring is not an exact amount of bytes)

What it means

bytes() in sha256lib.rb converts a binary input to an Array of byte integers, but only when the bit count after the 0b prefix is an exact multiple of 8 (bin.size % 8 == 0, one byte per 8 bits). When it is not, the function does not exit — it assigns this parenthesised note, a String, and returns it in place of the byte Array. Callers that assume an Array then fail later and elsewhere, typically with NoMethodError (undefined method 'map' for a String), so the root cause is easy to lose.

Source

Thrown at sha256lib.rb:102

		  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
    end
  when "hex"
    hex = input[2..-1] # trim 0x prefix
    bytes = [hex].pack("H*").unpack("C*") # convert hex string to bytes
  else
    bytes = input.bytes # convert ASCII string to bytes
  end

  return bytes
end

# ----------
# Operations
# ----------
# Addition modulo 2**32
def add(*x)
  total = x.inject(:+)
  return total % 2 ** 32 # limits result of addition to 32 bits

View on GitHub (pinned to 871e976d69)

Solutions

  1. Pad the bit payload with leading zeros to a whole number of bytes: 0b101 becomes 0b00000101
  2. When the bits came from an integer, format in byte units up front: x.to_s(2).rjust(((x.bit_length + 7) / 8) * 8, '0')
  3. If an odd bit count means a character was lost in transit, re-derive the value instead of padding blindly
  4. In your own wrapper, raise ArgumentError when (input[2..].size % 8) != 0 rather than consuming the sentinel String

Example fix

# before
bytes('0b101010101', 'binary')
# => '(bitstring is not an exact amount of bytes)' — a String, not data
bytes('0b101', 'binary').map { |b| b }
# => NoMethodError: undefined method 'map' for String

# after
bytes('0b0000000010101010', 'binary')
# => [0, 170]
Defensive patterns

Strategy: type-guard

Validate before calling

bits = input[2..].to_s
raise ArgumentError, 'bitstring must be a whole number of bytes, got ' + bits.size.to_s + ' bits' unless (bits.size % 8).zero?
byte_array = bytes(input, 'binary')

Type guard

def byte_array?(result)
  result.is_a?(Array) && result.all? { |b| b.is_a?(Integer) && b.between?(0, 255) }
end

result = bytes(input, input_type(input))
raise TypeError, 'bytes() returned a sentinel note instead of data: ' + result.inspect unless byte_array?(result)

Prevention

When it happens

Trigger: bytes('0b101', 'binary') returns the note String (3 bits); bytes('0b101010101', 'binary') likewise (9 bits); chaining .map onto either result raises NoMethodError in the caller. Through the bundled CLIs, sha256.rb with a non-byte-aligned binary input actually ignores the bytes() return value (it uses input[2..-1] as the message directly), so the note mostly bites scripts that require sha256lib.rb and call bytes() themselves.

Common situations: Typing tutorial example bit strings one bit off (7 or 9 instead of 8); converting integers with to_s(2) and forgetting to pad to whole bytes; scripting against sha256lib.rb as a library and mapping over bytes() output unconditionally.

Related errors


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