{"record":{"id":"ea1faedfe002479a","repo":"in3rsha/sha256-animation","slug":"bitstring-is-not-an-exact-amount-of-bytes","errorCode":null,"errorMessage":"(bitstring is not an exact amount of bytes)","messagePattern":"\\(bitstring is not an exact amount of bytes\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sha256lib.rb","lineNumber":102,"sourceCode":"\t\t  puts \"Invalid hex string: #{input}\"\n\t\t  exit\n\t\tend\n\t\treturn \"hex\"\n\t  else\n\t\treturn \"string\"\n\t  end\n  end\nend\n\n# Convert input (hex, ascii) to array of bytes\ndef bytes(input, type)\n  case type\n  when \"binary\"\n    bin = input[2..-1] # trim 0b prefix\n    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)\n      bytes = bin.scan(/.{8}/).map {|byte| byte.to_i(2)} # convert the bits to array of bytes (in decimal)\n    else\n      bytes = \"(bitstring is not an exact amount of bytes)\" # helpful note\n    end\n  when \"hex\"\n    hex = input[2..-1] # trim 0x prefix\n    bytes = [hex].pack(\"H*\").unpack(\"C*\") # convert hex string to bytes\n  else\n    bytes = input.bytes # convert ASCII string to bytes\n  end\n\n  return bytes\nend\n\n# ----------\n# Operations\n# ----------\n# Addition modulo 2**32\ndef add(*x)\n  total = x.inject(:+)\n  return total % 2 ** 32 # limits result of addition to 32 bits","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/in3rsha/sha256-animation/blob/871e976d69e66474ef5239ee82031b7d87cbcafc/sha256lib.rb#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pad the bit payload with leading zeros to a whole number of bytes: 0b101 becomes 0b00000101","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')","If an odd bit count means a character was lost in transit, re-derive the value instead of padding blindly","In your own wrapper, raise ArgumentError when (input[2..].size % 8) != 0 rather than consuming the sentinel String"],"exampleFix":"# before\nbytes('0b101010101', 'binary')\n# => '(bitstring is not an exact amount of bytes)' — a String, not data\nbytes('0b101', 'binary').map { |b| b }\n# => NoMethodError: undefined method 'map' for String\n\n# after\nbytes('0b0000000010101010', 'binary')\n# => [0, 170]","handlingStrategy":"type-guard","validationCode":"bits = input[2..].to_s\nraise ArgumentError, 'bitstring must be a whole number of bytes, got ' + bits.size.to_s + ' bits' unless (bits.size % 8).zero?\nbyte_array = bytes(input, 'binary')","typeGuard":"def byte_array?(result)\n  result.is_a?(Array) && result.all? { |b| b.is_a?(Integer) && b.between?(0, 255) }\nend\n\nresult = bytes(input, input_type(input))\nraise TypeError, 'bytes() returned a sentinel note instead of data: ' + result.inspect unless byte_array?(result)","tryCatchPattern":null,"preventionTips":["Always emit bit strings in whole-byte lengths — pad leading zeros up to the next multiple of 8","Never assume bytes() returns an Array; for misaligned binary input it returns a String note","Wrap bytes() with a strict shim that raises ArgumentError when the bit count is not a multiple of 8","Cover 1-, 7-, 8- and 9-bit inputs in tests so misalignment fails loudly in CI, not in production"],"tags":["ruby","sha256","binary","type-confusion","data-conversion"],"backgroundTag":"bit-length-not-byte-aligned","analyzedSha":"871e976d69e66474ef5239ee82031b7d87cbcafc","analyzedAt":"2026-08-23T12:17:23.862Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}