{"record":{"id":"0fd3921f2644b469","repo":"ruby/ruby","slug":"expected-n-bytes-got-str-inspect","errorCode":null,"errorMessage":"expected #{n} bytes, got #{str.inspect}","messagePattern":"expected #(.+?) bytes, got #(.+?)","errorType":"exception","errorClass":"Gem::SafeMarshal::Reader::DataTooShortError","httpStatus":null,"severity":"error","filePath":"lib/rubygems/safe_marshal/reader.rb","lineNumber":59,"sourceCode":"        raise UnconsumedBytesError, \"expected EOF, got #{@io.read(10).inspect}... after top-level element #{root.class}\" unless @io.eof?\n        root\n      end\n\n      private\n\n      MARSHAL_VERSION = [Marshal::MAJOR_VERSION, Marshal::MINOR_VERSION].map(&:chr).join.freeze\n      private_constant :MARSHAL_VERSION\n\n      def read_header\n        v = @io.read(2)\n        raise UnsupportedVersionError, \"Unsupported marshal version #{v.bytes.map(&:ord).join(\".\")}, expected #{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}\" unless v == MARSHAL_VERSION\n      end\n\n      def read_bytes(n)\n        raise NegativeLengthError if n < 0\n        str = @io.read(n)\n        raise EOFError, \"expected #{n} bytes, got EOF\" if str.nil?\n        raise DataTooShortError, \"expected #{n} bytes, got #{str.inspect}\" unless str.bytesize == n\n        str\n      end\n\n      def read_byte\n        @io.getbyte || raise(EOFError, \"Unexpected EOF\")\n      end\n\n      def read_integer\n        b = read_byte\n\n        case b\n        when 0x00\n          0\n        when 0x01\n          read_byte\n        when 0x02\n          read_byte | (read_byte << 8)\n        when 0x03","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/ruby/ruby/blob/0e5b888e1c355f3f728f2659f085820937dada48/lib/rubygems/safe_marshal/reader.rb#L41-L77","documentation":"DataTooShortError from SafeMarshal's read_bytes: the IO returned a non-nil string for a declared n-byte read but its bytesize < n, i.e. the stream ends mid-field. Distinct from the EOF case (nil return), this catches data that exists but is shorter than its length prefix claims, a classic sign of corruption in marshaled gem specifications.","triggerScenarios":"A length-prefixed field (string bytes, symbol name, ivar blob) whose prefix overruns the actual remaining data: e.g. the last string in a .gemspec declares 200 bytes but only 50 exist because the tail was overwritten or never written.","commonSituations":"Partially flushed cache files after a crash or kill during gem install; disk-full truncation; files mangled by an editor, encoding conversion, or transfer tool (FTP ASCII mode, bad UTF-8 sanitize).","solutions":["Regenerate the affected spec/gem: gem pristine <gem>-v <ver> or uninstall/reinstall the gem","Compare the file size with a known-good copy (same gem from rubygems.org) to confirm truncation; re-download if sizes differ","Audit scripts/tools that rewrite cache files in place; replace in-place writes with atomic write-then-rename","Verify no text-mode or encoding transformations touched the binary file in transit"],"exampleFix":"# before\nstr = File.binread(path, mode: 'rb')\nGem::SafeMarshal.safe_load(str) # DataTooShortError\n\n# after\nGem::Specification.from_yaml(File.read(path)) rescue (gem pristine ...) # or simply:\nspec = Gem::Package.new(redownloaded_gem).spec\n","handlingStrategy":"try-catch","validationCode":"def length_fields_fit?(path)\n  # cheap size sanity: compare against a freshly downloaded copy or stored checksum\n  digest = Digest::SHA256.file(path).hexdigest\n  digest == EXPECTED_DIGESTS[File.basename(path)]\nend","typeGuard":null,"tryCatchPattern":"begin\n  Gem::SafeMarshal.safe_load(data)\nrescue Gem::SafeMarshal::Reader::DataTooShortError\n  restore_file # delete + re-download / gem pristine\nend","preventionTips":["Checksum gem artifacts at fetch time and re-verify before parse","Never let text-mode IO or encoding conversion touch binary caches (binmode everywhere)","Store expected sizes/digests alongside cached files"],"tags":["rubygems","safe-marshal","truncated-data","length-mismatch"],"backgroundTag":"corrupt-serialized-data","analyzedSha":"0e5b888e1c355f3f728f2659f085820937dada48","analyzedAt":"2026-08-21T14:25:43.473Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}