はじめに
自作サービス(絶望えらぁ短歌)で投稿内容に応じた動的OGP画像を生成する機能を実装した際、いくつかのエラーやハマりポイントに遭遇しました。
この記事では、発生したエラーの原因分析から解決までのプロセスを備忘録としてまとめます。
検証環境
📝 のちの自分用メモ:バージョンが変わると再現しない/しなくなるエラーもあるため、当時の環境を残しておく。
| 項目 | バージョン |
|---|---|
| Ruby | 3.3.0 |
| Rails | 8.1.3.1 |
| ImageMagick | 7.1.1-43 |
| mini_magick (gem) | 5.3.3 |
| デプロイ先 | Render |
1. RecordNotFound(IDの不整合)
発生した現象・初期の仮説
OGP画像の生成リクエスト時にデフォルト画像にフォールバックしてしまい、ログに以下が出力された。
OGP Generation Failure: ActiveRecord::RecordNotFound - Couldn't find Post with 'id'="1"
- 初期仮説1: OGP生成処理(コントローラのアクションやルーティング)自体にバグがあり、正しいIDを渡せていないのではないか?
- 初期仮説2: ID体系ではなく、この時点ではまだ疑っていなかったMiniMagick/ImageMagick側の処理でエラーが起きているのではないか?
検証と真因
-
検証: Railsコンソールで
Post.pluck(:id)を実行し、実際に存在するIDを確認したところ、そもそもid: 1のレコード自体が存在しないことが判明。ロジックのバグではなくデータ側の問題だと特定した。 -
真因: 開発用データベースでデータの削除・追加を繰り返していたことで
id: 1のレコードが存在しなくなっていたにもかかわらず、リクエスト側はid: 1決め打ちのままだったため。
🔍 見分け方:
Couldn't find XXX with 'id'=が出たら、まずロジックのバグより先にDBの中身(seedやテストデータの削除漏れ)を疑う。
対策
Railsコンソールで実際のIDを確認し、存在するデータに対してリクエストを行うように修正。
# Railsコンソールで存在するIDを確認
Post.pluck(:id)
# => [2, 3, 4, 5, ...]
2. MiniMagickのエラー(Errno::ENOENT / no encode delegate for this image format 'CANVAS')
発生した現象・初期の仮説
MiniMagick::Image.open("xc:#050202") を実行した際、Errno::ENOENT や no encode delegate for this image format 'CANVAS' が発生した。
- 初期仮説1: ローカルとDocker環境でImageMagickのバージョンやDelegate(PNG等のエンコーダ)のインストール状態が異なるのではないか?
-
初期仮説2:
xc:記法のフォーマット指定の問題ではなく、コンテナ内のImageMagickが壊れているのではないか?
検証と真因
-
検証: コンテナ内で
convert --versionを確認したところ、Delegatesにpngやfreetypeが正しく組み込まれていることを確認(ImageMagick 7.1.1-43)。環境の問題ではなくコード側の記述問題だと特定した。 -
真因:
-
MiniMagick::Image.openは既存ファイルを開くメソッドのため、xc:をファイルパスとして処理しようとしてErrno::ENOENTになる。 -
Tool::Convertに切り替えた際も、一時ファイル(f.path)に拡張子(.png)が付与されないため、ImageMagickが「フォーマット不明(CANVAS)」と判定して出力エラーになる。
-
🔍 見分け方:エラーメッセージだけ見ると環境依存を疑いたくなるが、
convert --versionでDelegateの有無を確認できれば、環境要因かコード要因かを素早く切り分けられる。
対策
入力側の xc:(単色キャンバス生成)と、出力先パスへの png:(PNGエンコードの明示)を両方指定することで完全解決した。
# ⭕ 正しいコマンド構築
MiniMagick::Tool::Convert.new do |config|
config.size "1200x630"
config << "xc:#{OGP_BG_COLOR}"
config << "png:#{f.path}" # 出力フォーマットを明示
end
最終的な実装(修正後コード全体)は以下の通り。
def ogp
post = Post.find(params[:id])
tanka_text = (post.tanka.presence || "デフォルトテキスト").tr("\n", " ")
author_text = "詠み手:#{post.author_name.presence || '名無し法師'}"
# 単色背景のキャンバス画像を生成(xc: と png: を明示)
image = MiniMagick::Image.create do |f|
MiniMagick::Tool::Convert.new do |config|
config.size "1200x630"
config << "xc:#{OGP_BG_COLOR}"
config << "png:#{f.path}" # png形式を明示
end
end
# テキストの描画
image.combine_options do |c|
c.fill OGP_TEXT_COLOR
c.pointsize "38"
c.gravity "center"
c.draw "text 0,-30 #{tanka_text.inspect}"
c.fill OGP_AUTHOR_COLOR
c.pointsize "24"
c.draw "text 0,150 #{author_text.inspect}"
end
send_data image.to_blob, type: "image/png", disposition: "inline"
rescue StandardError => e
Rails.logger.error("OGP Generation Failure: #{e.class} - #{e.message}")
send_file Rails.root.join("app/assets/images/default_ogp.jpg"), type: "image/jpeg", disposition: "inline"
end
3. X(旧Twitter)でOGPカードが表示されない問題
発生した現象・初期の仮説
Render(本番環境)へデプロイ後、Xでツイートしても画像が表示されずリンクだけになる。
-
初期仮説1: RenderのLinuxサーバー上に
ImageMagickやフォントが入っておらず、本番でのみMiniMagick::Errorが発生してデフォルト画像(またはエラー)になっているのではないか? -
初期仮説2: OGP画像のメタタグ(
og:image)のURLがhttp://になっており、Xのクローラーに拒否されているのではないか?
検証と真因
-
検証:
- 本番の画像URL(
https://despair-tanka.onrender.com/posts/xx/ogp)へ直接アクセス → 正常に綺麗な画像が表示される(サーバー・コードは正常)。 - OGP確認ツール(Card Validator等)でURLを検証 → 正常に画像カードが生成される。
- 本番の画像URL(
- 真因: アプリ開発途中(画像が正しく生成できていなかった頃)にXのクローラーがそのURLへアクセスした際の「失敗データ(または画像なし状態)」が、X側に強力にキャッシュされていた。
🔍 見分け方:実装・レスポンス自体(curlやブラウザでの直接アクセス)は正常なのにX上だけ反映されない場合は、まずクローラー側のキャッシュを疑う。
対策
X Card Validator 等の確認ツールで対象URLを一度読み込ませることで、X側のクローラーキャッシュを強制的・即時に最新の動的OGP画像へ更新させた。
4. CI(GitHub Actions)のハマりポイント
リリース直前、CIが赤(Failure)になったため以下の対応を実施。
① Lint(RuboCop)エラー
-
現象: 末尾の不要な空白(
Layout/TrailingWhitespace)でCIが赤に。 -
対策:
bundle exec rubocop -aで自動修正。
② Gitコミット時の -m 忘れ
-
現象:
git commit "メッセージ..."を実行した際、pathspec ... did not match any file(s)エラーが発生。 -
原因:
-mオプションを付け忘れたため、Gitがメッセージを「削除・変更対象のファイル名(pathspec)」だと誤認識した。 -
対策:
git commit -m "メッセージ"で正しく指定。
③ BrakemanのGem更新で frozen mode に弾かれる
-
現象:
bundle update brakemanを実行してもversion stayed the sameとなり更新されず、Dockerコンテナ内ではthe lockfile can't be updated because frozen mode is setが発生。 -
原因:
- Gemfileにバージョン指定(
>= 8.0.6)がなかったためBundlerが更新をスキップしていた。 - Docker環境のBundlerで
frozen mode(Gemfile.lockの変更禁止設定)が有効になっていた。
- Gemfileにバージョン指定(
-
対策:
- Gemfileに
gem "brakeman", ">= 8.0.6", require: falseを明記。 -
docker compose exec web bundle config set frozen falseで一時的にロックを解除してからbundle updateを実行。
- Gemfileに
④ test/system 不在による LoadError
-
現象:
bin/rails test:system実行時にcannot load such file -- .../test/system (LoadError)が発生。 -
原因:
bin/ciスクリプト内ではシステムテストがコメントアウトされていたが、GitHub Actions(ci.yml)側ではシステムテストが起動するように記述されており、かつリポジトリ内にtest/systemディレクトリ自体が存在しなかった。 -
対策:
mkdir -p test/system && touch test/system/.keepで空ディレクトリと.keepファイルを作成・コミットして解消。
エラー早見表(サマリー)
| # | エラーメッセージ(抜粋) | 原因 | 解決策 |
|---|---|---|---|
| 1 | Couldn't find Post with 'id'=... |
存在しないIDへのリクエスト | 実在するIDでリクエスト/DBの状態を確認 |
| 2 | Errno::ENOENT ... xc:#050202 |
MiniMagick::Image.open がカラー指定をファイルパスと誤認識 |
MiniMagick::Tool::Convert でコマンド組み立て |
| 2 | no encode delegate for this image format 'CANVAS' |
一時ファイルに出力フォーマット未指定 |
xc: と png: を明示指定 |
| 3 | Xでカード非表示(テキストリンクのみ) | Xクローラーの古いキャッシュ | Card Validatorで再Fetch |
| 4 | pathspec ... did not match any file(s) |
git commit で -m を付け忘れ |
git commit -m "..." で正しく指定 |
| 4 | the lockfile can't be updated because frozen mode is set |
Gemfileのバージョン未指定+Bundlerのfrozen mode | バージョン指定を明記し bundle config set frozen false
|
| 4 | cannot load such file -- .../test/system (LoadError) |
test/system ディレクトリの不在 |
mkdir -p test/system && touch test/system/.keep |
おわりに
ImageMagickやMiniMagickのコマンド組み立ては独自のハマりどころが多く、ログを正確に読み解く重要性を改めて実感しました。
同じようなエラーで詰まっている方の参考になれば幸いです!