🎏

濁点入りのファイル名をもつmacOSアプリをFinderでコピーすると壊れていると表示されて起動できなくなる現象とUTF-8-MAC問題

に公開

発端

先日、azoo-key-skkservというOSSのmacOS GUI版の開発をしていたときのことです。リリースビルドしたアプリケーションバンドルを含むdmgを作り、Apple Notary Serviceで公証を通したバイナリを一緒に開発しているユーザーにテストしてもらいました。すると不思議なことにdmgをマウントした状態なら問題なく起動するのですが、仮想ディスクから /Applications にコピーして開こうとすると「"azoo-key-skkserv" is damaged and can't be opened. You should move it to the Trash.」というダイアログが出てしまい起動できないのです。
(日本語環境では "azoo-key-skkserv" は壊れているため開けません。ゴミ箱に入れる必要があります。 というエラーメッセージになります)

"azoo-key-skkserv" is damaged and can't be opened. You should move it to the Trash.

macOSユーザーの方ならdmgをダウンロードしてマウントしたボリューム上にアプリアイコンと /Applications へのリンクだけがあるのを見たことがあるのではないでしょうか。今回やったことはそれと同じようにdmgをマウントしてアプリケーションバンドルを /Applications のアイコンにドラッグしてコピーしただけです。たったそれだけの操作でアプリケーションが壊れる (と表示される) のは一体どういうことなのでしょうか。

アプリが壊れてしまう問題を調査

"XXX is damaged and can't be opened. You should move it to the Trash." というエラーメッセージはアプリケーションの署名が不正だと判定されたときに表示されるようです。そこで codesign コマンドでなぜ署名が不正と判定されたのかを調べてみましょう。codesignの引数をあれこれぐぐったりしながら試行錯誤したところ、/Applications にFinderでコピーした方のアプリケーションバンドルだけエラーを出すことがわかりました。

dmgをマウントしたとき: 正常

❯ codesign --verify --deep --verbose /Volumes/azoo-key-skkserv/azoo-key-skkserv.app
/Volumes/azoo-key-skkserv/azoo-key-skkserv.app: valid on disk
/Volumes/azoo-key-skkserv/azoo-key-skkserv.app: satisfies its Designated Requirement

/Applications にコピーしたとき: file added & file missing エラー

❯ codesign --verify --deep --verbose /Applications/azoo-key-skkserv.app
/Applications/azoo-key-skkserv.app: a sealed resource is missing or invalid
file added: /Applications/azoo-key-skkserv.app/Contents/Resources/AzooKeyKanakanjiConverter_KanaKanjiConverterModuleWithDefaultDictionary.bundle/Contents/Resources/Dictionary/louds/グ1.loudstxt3
(以下濁点入りのファイルが合計74ファイル並ぶ)
file missing: /Applications/azoo-key-skkserv.app/Contents/Resources/AzooKeyKanakanjiConverter_KanaKanjiConverterModuleWithDefaultDictionary.bundle/Contents/Resources/Dictionary/louds/グ1.loudstxt3
(以下濁点入りのファイルが合計74ファイル並ぶ)

どうやら/Applications にコピーした方には グ1.loudstxt3 といった必要なファイルが見当たらない & グ1.loudstxt3 といった署名されてないファイルが含まれているようです。どちらも同じファイル名に見えますが何が起きているのでしょうか?

原因はFinderによるNFD正規化

macOSで濁点が含まれるファイル名で起きるエラーといえば、ファイル名のUTF-8-MAC/NFD正規化が怪しいです。

NFD正規化というのはUnicode正規化の一つで、濁点、半濁点などのファイル名について2コードに分けます。例えば「ガ」という文字は濁点付きの1コード (U+30AC) で表現することもできますし、「カ (U+30AB)」に「結合用の濁点 (U+3099)」の2コードに分けることもできます。(結合用の濁点は、単体濁点である U+309B とは別)
どちらも同じ「ガ」という字であることは String.prototype.normalize()の結果が等しくなることからもわかります。

// JSによるスニペット。
console.log("\u30ac", "\u30ab\u3099")
// ガ ガ
"\u30ac" === "\u30ab\u3099"
// → false
// `String.prototype.normalize()` でUnicode正規化すると等しくなる
"\u30ac".normalize() === "\u30ab\u3099".normalize()
// → true

// Intl.Segmenterを使って数えるとどちらも1文字と分かる
const segmenter = new Intl.Segmenter("ja-JP", { granularity: "grapheme" })
[...segmenter.segment("\u30ac")].length
// 1
[...segmenter.segment("\u30ab\u3099")].length
// 1

今回確認したのはOSはmacOS 15.5 / 15.6で、dmgもシステムディスクもAPFS形式のファイルシステムでした。
APFSはWindows, Linuxと同様に通常はファイル名のUnicode正規化を行いません。ですがFinderでファイルコピーを行うとファイル名のUTF-8-MAC正規化が行われます[1]

リソースの署名情報はアプリケーションバンドルの XXXX.app/Contents/_CodeSignature/CodeResources に記載されています。

CodeResources
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>files</key>
        <dict>
                <key>Resources/AppIcon.icns</key>
                <data>
                t0PmF6s3woizq4VbkVnofHdHzgU=
                </data>
                (...中略)
                <key>Resources/AzooKeyKanakanjiConverter_KanaKanjiConverterModuleWithDefaultDictionary.bundle/Contents/Resources/Dictionary/louds/グ1.loudstxt3</key>
                <data>
                yFsHBbscrm5IQ7ZYLjnqcPTyGF4=
                </data>
                (...中略)

CodeResources ファイル自体はFinderコピーで中身が変わるわけではないのですが、FinderコピーによりUTF-8-MAC正規化でリソースファイル名が変更されてしまうと、どうやら存在するべきリソースファイルがない (& 署名されてないリソースファイルが存在している) という扱いをされてしまうようです。

ということで、dmgから直接起動するときとFinderでdmgからコピーするときで挙動が壊れる違いはファイル名のUTF-8-MAC正規化が発生するかどうかの違いで起きることがわかりました。

  • dmgをマウントしているときはファイル名の正規化が起きていないため、署名は壊れておらず正常に起動できる
  • dmgからFinderでコピーすると、ファイル名のUTF-8-MAC正規化がされて、署名されたファイル名と実際のファイル名が異なってしまい、壊れていると表示される

なおファイル名のUTF-8-MAC/NFD正規化をせずにコピーすることでこの問題は回避できます。
例えばdittoコマンドによるファイルコピーを行えばアプリを開くときもエラーダイアログは表示されません。

❯ ditto /Volumes/azoo-key-skkserv/azoo-key-skkserv.app /Applications/azoo-key-skkserv.app

❯ codesign --verify --deep --verbose /Applications/azoo-key-skkserv.app
/Applications/azoo-key-skkserv.app: valid on disk
/Applications/azoo-key-skkserv.app: satisfies its Designated Requirement

なお、Finderによるファイル名のUTF-8-MAC正規化を含めて、今回の問題の調査には 誤解の多い「NFD問題とUTF-8-MAC問題」を解説する - macOSの濁点を含むファイル名の扱い という記事を非常に参考にさせていただきました。この場を借りてお礼申し上げます。

Apple Developer Forumsに報告。しかし…

起動できなくなる原因はひとまずわかりましたが、すべてのazoo-key-skkserv利用ユーザーに「dmgからFinderでコピーしないで下さい」と説明するのは難しいです。

そこで再現環境を作り、Apple Developer Forumsで報告することにしました。

https://github.com/mtgto/example-utf8-mac-notarization
このリポジトリにソースコードとdmg作成のMakefile, GitHub Releasesにdmgをアップロードしてあります。

ちなみにアプリケーション自体のリソース (example-utf8-mac-notarization.app/Contents/Resources 直下に置かれる) に濁点入りのファイル名を持たせたときはFinderでコピーしても署名エラーはなぜか発生せず、依存するSwift Packageが濁点入りのファイル名を持つようにしたらエラーが発生しました (example-utf8-mac-notarization.app/Contents/Resources/Foo_Foo.bundle/Contents/Resources 以下に置かれる)。

再現環境が用意できたので、今回発生した問題をApple Developer Forumsで報告しました。

https://developer.apple.com/forums/thread/796364

報告すると、Apple DTS Engineerの方から返信がありました。回答をまとめると、

  • この問題は既存のバグであり未解決である。
  • 修正予定は立っていない。
  • Radar (r. 68829319 ) は2020年に記録された。
  • アプリケーションバンドル内にもつファイル名は問題がないASCIIに変更することを推奨する。

とのことでした。修正目処も立ってないのは結構きついですね…😇

なお、我々の環境では配布形式をpkgにして /Applications にインストーラーでインストールするようにしたところ問題が起きないようだったのでそうしたんですが、Appleのエンジニアからは「AirDropやzip/unzipなどで再度壊れることもありうるのでファイル名をASCIIに統一する方がいい」との回答をいただきました。

まとめ

macOS 15.6 + APFSの環境でファイル名に濁点や半濁点を含むリソースをもつアプリケーションをFinderでコピーすると署名エラーで起動できなくなるという問題があることがわかりました。
この問題はmacOSの不具合が原因ですが2025年8月現在修正の目処は立っていません。macOSアプリをMac App Store外で配布するときにはこの問題を回避するためにファイル名はASCIIコードだけに統一しておくとよいでしょう。

関連リンク

脚注
  1. NFD正規化とUTF-8-MAC正規化は影響する文字種が若干異なりますが、かな + 濁点を別コードにするのはどちらも同じなのでこの記事の中でもNFD正規化っていったりUTF-8-MAC正規化っていったりしちゃっています🙇 ↩︎

Discussion