冒頭まとめ

invalid reference format は、イメージの指定が文法に合っていない、という判定です。重要なのは、この判定がレジストリへ問い合わせる前に手元で行われることです。通信は一切発生していないので、存在しないイメージを指したときの応答(404)とは別の段階の話になります。認証や通信経路を疑っても意味がありません。

文言は2種類あり、意味がはっきり違います。invalid reference format だけの場合と、invalid reference format: repository name must be lowercase と続く場合です。この2つは、Dockerイメージ名を解析する部分のソースで、明確に別の値として定義されています。解析の流れは、まず文字列を文法と照合し、合わなければ全体を小文字にしてもう一度照合する、という順です。小文字にすれば通る場合だけ「小文字でなければならない」という文言を返し、それ以外はすべて一般の文言になります。

この違いは、原因を絞るのにそのまま使えます。小文字を求める文言が出たということは、Docker が受け取った文字列は「大文字さえ無ければイメージ名として成立していた」ということです。イメージ名を大文字で書いた覚えがないのにこれが出るなら、渡ってしまったのは大文字を含む別の何か、たとえばファイルパスである可能性が高くなります。

そして、実務で最も多い原因はイメージ名そのものではありません。シェルDocker に渡した文字列が、書いたつもりのものと違っているという形です。したがって最初にやるべきは、名前を直すことではなく、実際に何が渡ったかを確かめることです。

エラーの概要

実行時の出力はこの形です。

docker: invalid reference format.
See 'docker run --help'.

小文字を求める場合はこうなります。

docker: invalid reference format: repository name must be lowercase.
See 'docker run --help'.

解析部分のソースには、この判定に関わるエラーが5つ定義されています。文法に合わない場合、小文字でない場合、名前が空の場合、名前が長すぎる場合、タグの書式が不正な場合です。名前の長さの上限は255文字と定義されています。

文法そのものも定義を読むと明快です。名前の各部分は小文字の英数字で始まり、区切りとして点1つ、下線1つか2つ、連続する横棒が使えます。それらを斜線でつないだものが名前です。一方、タグは英数字か下線で始まり、以降に点と横棒を含められ、全体で128文字までです。つまりタグには大文字を使えます。

この非対称は覚えておく価値があります。myapp:V1.0 は通り、MyApp:v1.0 は通りません。名前は小文字だけ、タグは大文字も可、という組み合わせです。

もう1つ、レジストリの指定にも規則があります。名前の先頭部分がレジストリとして扱われるのは、点を含むか、コロンとポート番号が付く場合、そして localhost の場合だけです。定義にもそう書かれています。点もポートも含まない語は、レジストリではなく名前の一部として扱われます。

まず最初に:渡った文字列を確認する

第一に、文言のどちらが出ているかを見ます。小文字を求める文言なら、渡った文字列は大文字を含む何かです。一般の文言なら、小文字にしても通らない文字列です。

第二に、実際に渡った引数を表示させます。シェルの展開を経た後の姿を見るのが目的です。

set -x
docker run --rm -v "$(pwd)":/app "$IMAGE:$TAG"
set +x

第三に、空の変数を疑います。名前かタグのどちらかが空になると、このエラーになります。次の3つはいずれも同じ結果です。

docker run :latest
docker run debian:
docker run :

第四に、コマンドを1行に書き直して実行してみます。行の折り返しに使う記号のうしろに余計な空白が入っていると、そこで行が終わったことにならず、想定外の引数が生まれます。この形は目視で見つけにくいので、1行にまとめると再現しなくなることで気付けます。

よくある原因と解決手順

原因1:シェルの引用が足りない

最も多い形です。パスに空白が含まれていると、囲っていない場合にシェルがそこで区切ってしまい、後ろの断片がイメージ名の位置に入り込みます。

Before(囲っていない):

docker run -v $(pwd):/app example:1.0
# 現在地に空白が含まれると、断片がイメージ名として扱われる

After(囲う):

docker run -v "$(pwd)":/app example:1.0

Windows や macOS では、利用者のファイル置き場のパスに大文字が含まれることが多く、その断片がイメージ名の位置に入ると、小文字を求める文言のほうが出ます。「イメージ名は小文字で書いているのに小文字にしろと言われる」という状況になったら、まずこの形を疑ってください。

変数を使う箇所は、すべて二重引用符で囲うのが安全です。

docker run -v "$HOST_DIR":/app "$IMAGE:$TAG"

原因2:変数が空のまま渡っている

$IMAGE$TAG が設定されていないと、:latestexample: の形になり、同じエラーになります。書き間違いや、別のコマンドの出力から値を取っていて空になった場合に起きます。

Before(空でも気付かず進む):

TAG=$(get_version)   # 失敗すると空になる
docker run "example:$TAG"

After(空なら止める):

TAG=$(get_version)
docker run "example:${TAG:?タグが空です}"

${変数:?メッセージ} の書き方は、値が空か未設定のときにシェルが止めてくれます。どこで空になったのかが分かるため、原因の特定が早くなります。

原因3:名前に使えない文字が入っている

イメージ名に大文字を使った場合は、小文字を求める文言が出ます。これは素直に小文字へ直します。

Before:

docker build -t MyApp:v1.0 .

After:

docker build -t myapp:v1.0 .

大文字以外の使えない文字、たとえば空白や記号が入っている場合は、小文字にしても通らないため一般の文言になります。使えるのは小文字の英数字と、区切りとしての点・下線・横棒だけです。区切りは各部分の先頭や末尾には置けません。-myappmyapp- は通りません。

タグの側は制限が緩く、大文字も使えます。ただし先頭に点や横棒は置けず、全体で128文字までです。日付や版番号をそのまま使う場合は、先頭の文字に注意してください。

原因4:レジストリのつもりの語が名前として扱われている

これはエラーにはなりませんが、混乱の元なので挙げておきます。前述のとおり、先頭部分がレジストリとして扱われるのは、点を含むか、ポート番号が付くか、localhost の場合だけです。

docker pull myregistry/app:1.0
# → myregistry は登録先ではなく、名前の一部として扱われる

社内のレジストリを指したいなら、点を含む名前かポート番号を付けます。

docker pull myregistry.example.com/app:1.0
docker pull myregistry:5000/app:1.0

意図した先から取得できていないときは、この規則を思い出してください。

補足:似ているが別のもの

invalid volume specification は、-v に渡した内容の書式が不正な場合のエラーです。文言が違うので区別できます。この場合、イメージ名の位置までは正しく解析されていて、問題は割り当ての指定の側にあります。

存在しないイメージを指した場合は、文法としては正しいので、このエラーにはなりません。レジストリへ問い合わせた結果として、見つからない旨が返ります(Docker の 404 の記事)。文法で弾かれる段階と、問い合わせた結果の段階は別だ、という点が両者の境界です。

要求の書式が不正で常駐している側から返るエラーは 400 です(Docker の 400 の記事)。取得の途中で時間切れになる場合はまた別です(Docker の context deadline exceeded の記事)。いずれも、通信が発生している点で本記事のエラーとは段階が異なります。

切り分けの順序

  1. 文言を確認する。repository name must be lowercase が付くなら、渡った文字列は大文字を含み、小文字にすれば通るものだった。
  2. イメージ名を大文字で書いていないなら、イメージ名以外の何かが渡っていると考える。多くはファイルパス
  3. 実行を追跡する設定を有効にして、展開後の引数を目で確認する。
  4. 変数が空になっていないかを確かめる。名前かタグが空でも同じエラーになる。
  5. 変数を含む箇所をすべて二重引用符で囲う。特に現在地や置き場所を渡す箇所。
  6. コマンドを1行にまとめて実行し、再現するかを見る。再現しないなら、行の折り返しの周辺に余計な空白がある。
  7. 名前とタグの規則で確認する。名前は小文字のみ、タグは大文字可、先頭に区切り記号は置けない。

確認コマンド集

# 1. 展開後の引数を表示して実行する
set -x
docker run --rm "$IMAGE:$TAG"
set +x

# 2. 空の変数を検出する(空なら実行前に止まる)
docker run --rm "${IMAGE:?イメージ名が空です}:${TAG:?タグが空です}"

# 3. 変数の中身を見えない文字ごと確認する
printf '%q\n' "$IMAGE" "$TAG" "$(pwd)"

# 4. 名前が規則に合うかを手元で試す(取得はせず解析だけ確認する)
docker image inspect "$IMAGE:$TAG" 2>&1 | head -3

# 5. 手元にある名前の一覧と見比べる
docker images --format '{{.Repository}}:{{.Tag}}'

# 6. コマンド全体を1行にして再現するか確かめる
docker run --rm -v "$(pwd)":/app example:1.0

Editor’s Note

このエラーの性質をよく表した記録として、2022年8月に公開された調査の記事があります(One time when it really was a Docker command quoting issue)。筆者は、行を折り返して書いた実行コマンドinvalid reference format で失敗する状況に遭遇しました。

調査の過程が参考になります。まず、囲っていない変数が複数あることに気付きますが、中身に空白が無いことを確認できたため、引用の問題ではないと判断します。次に、名前かタグが空だと同じエラーになることを実際に試して確かめます。しかし、直前の取得のコマンドは同じ変数を使って成功していたため、空でもないと分かります。見えない文字が混じっている可能性も、専用の道具で確認して否定します。

答えは、行の折り返しに使う記号のうしろに空白が1つ入っていたことでした。そのため折り返しが成立せず、空白1文字が最初の位置引数、つまりイメージ名として渡っていたのです。筆者は最後に、記号による空白の打ち消しはシェルの説明では引用の項に分類されるので、結局これも引用の問題だった、と書いています。

この記録が示すのは、Docker が拒否した文字列は、書いたつもりの文字列とは限らない、ということです。名前の規則を何度読み直しても、渡っていたのが空白1文字では意味がありません。このエラーに当たったら、名前を直す前に、実際に渡った文字列を表示させてください。それが最短の道です。


免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。