Windows/Cygwin で gem install/update が失敗する

プログラミングの話題を書くのは久々な気がします。

先ほど、何となく「Windows/Cygwin の方の gem もアップデートしておくかぁ」と思い立ちました。ここで無事にアップデート完了したまでは良かったのですが、それ以降 gem install/update と叩くと、パッケージに関わらず常に「invalid byte sequence in UTF-8」のエラーが返ってくると言う状況に陥りました。

clown@guarneri ~
$ ruby --version
ruby 1.9.3p484 (2013-11-22) [i386-cygwin]

clown@guarneri ~
$ gem --version
2.2.2

clown@guarneri ~
$ locale
LANG=ja_JP.UTF-8
LC_CTYPE="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_ALL=

上記の環境で実行した際に表示されたバックトレースは以下の通りです(一部、省略)。

clown@guarneri ~
$ gem update nokogiri
Updating installed gems
ERROR:  While executing gem ... (ArgumentError)
    invalid byte sequence in UTF-8
        /usr/lib/ruby/1.9.1/win32/registry.rb:174:in `tr'
        /usr/lib/ruby/1.9.1/win32/registry.rb:174:in `initialize'
        /usr/lib/ruby/1.9.1/win32/registry.rb:232:in `exception'
        /usr/lib/ruby/1.9.1/win32/registry.rb:232:in `raise'
        /usr/lib/ruby/1.9.1/win32/registry.rb:232:in `check'
        /usr/lib/ruby/1.9.1/win32/registry.rb:285:in `QueryValue'
…(中略)…
        /usr/lib/ruby/site_ruby/1.9.1/rubygems/command_manager.rb:137:in `run'
        /usr/lib/ruby/site_ruby/1.9.1/rubygems/gem_runner.rb:54:in `run'
        /usr/bin/gem:21:in `<main>'

めんどくさくなったので放置しようかとも思いましたが……一応エラーを追ってみると、何らかの別の理由で例外が発生してエラーメッセージを取得する際にコケていました。

# /usr/lib/ruby/1.9.1/win32/registry.rb:168
def initialize(code)
  @code = code
  msg = "\0".force_encoding(Encoding::ASCII_8BIT) * 1024
  len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0)
  msg = msg[0, len].force_encoding(Encoding.find(Encoding.locale_charmap))
  super msg.tr("\r", '').chomp
end

どうやら、取得したメッセージ (CP932) を強制的に UTF-8 として認識させた上で改行コードを取り除こうとしたため「invalid byte sequence in UTF-8」が発生したと言うもののようです。今回のエラー自体は rubygems.org の名前解決時に発生していたようですが、それ以外の場合でも、レジストリから値を取得するのに失敗した時には同様のエラーが出そうな予感がします。

修正方法ですが、現状は常に "error" と言う文字列を返すと言う思考停止な方法でスルーしています……