{"thread":{"id":"65858","subject":"Fetching missing submodule refs unnecessarily fatal","startedAt":"2026-06-23T15:03:52Z","lastAt":"2026-06-23T15:03:52Z","messageCount":1,"participants":["Mike Crowe"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"546237","messageId":"ajqX5FOz6tsQqvlI@mcrowe.com","threadId":"65858","inReplyTo":null,"subject":"Fetching missing submodule refs unnecessarily fatal","fromName":"Mike Crowe","fromEmail":"mac@mcrowe.com","sentAt":"2026-06-23T14:27:48Z","receivedAt":"2026-06-23T15:03:52Z","isPatch":false,"body":"When Git fetches in a superproject with --recurse-submodules, it appears to\ntry to fetch the corresponding submodule repository commits for every new\nor updated superproject branch. Presumably this is so that everthing is\nready to switch to one of those branches without further fetching.\n\nDevelopers may create commits that contain submodules that reference\ncommits in the submodule repository, but those commits may not be pushed to\nthe submodule's remote repository. When the superproject commits are pushed\nto a personal remote branch anyone else's Git fetch cannot find the\ncorresponding submodule commit and fails. For example:\n\n $ git fetch\n remote: Enumerating objects: 4, done.\n remote: Counting objects: 100% (4/4), done.\n remote: Compressing objects: 100% (2/2), done.\n remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)\n Unpacking objects: 100% (3/3), 355 bytes | 355.00 KiB/s, done.\n From ssh://localhost/home/mac/git/git/repro/repositories/super\n  * [new branch]      repro-branch -> origin/repro-branch\n Fetching submodule submodule\n error: Server does not allow request for unadvertised object f6b0ccce6e2085cf03c3fd924730f5c9f91e3db1\n Errors during submodule fetch:\n         submodule\n\n(when fetching via ssh)\n\nor:\n\n fatal: remote error: want c1f59d10bd6f24adbc96fee6a5041e9f3dc94b7c not valid\n fatal: internal server error\n fatal: remote error: want f91af98469911e79c2a27329d8e115dfc59f31c0 not valid\n fatal: internal server error\n Errors during submodule fetch:\n \tsources/repo-1\n \tsources/repo-2\n\n(when using a JGit server configured to allow any SHA-1 to be fetched.)\n\nThese are hard errors that cause Git to exit with a non-zero exit status.\nRepeating the operation succeeds because no there is no update to the\nremote branch to trigger the submodule fetch again.\n\nI've had a couple of goes at bisecting this but I always end up failing on\nunrelated commits due to the master/main default branch change and my\nattempts to work around that produce inconsistent results.\n\nI don't believe that developers who have the ability to create personal\nbranches should be able to force anyone else cloning or fetching from the\nrepository to suffer such a failure. This is particularly a problem for CI\nsystems but it confuses users too.\n\nPotential mitigations:\n\n1. Use --no-recurse-submodules. This disables all submodule processing\n   though, which is not desirable.\n\n2. Use `git fetch || git fetch` to repeat the fetch if it fails the first\n   time. This will work around the problem almost all of the time but is\n   racy since there's a small risk that the second fetch will encounter a\n   new branch with the same problem.\n\nI've added a script which can be used to reproduce the problem to the end\nof this message.\n\nI'm not really sure what a good solution to this is:\n\n1. The recursive fetch could only look for submodule commits to fetch on\n   the current tracking branch.\n\n2. Treat any failure to fetch submodules as non-fatal. Hacking\n   fetch_submodules() to always return zero does solve this problem but at\n   the cost of not failing for more-serious ones! Any attempt to check out\n   the unfetchable commit would fail at that point though.\n\nThanks.\n\nMike.\n\n--8<--\n#!/bin/bash\nset -xe\ntemp=$(pwd)/repro\n\nrm -rf ${temp}\nmkdir ${temp}\nrepositories=${temp}/repositories\n\n# Work around protocol.file.allow = \"user\" by default in new\n# repositories without affecting the user's global Git configuration\n# by fetching over ssh from localhost.\nremote=ssh://localhost${repositories}\n\n# Create the \"remote\" repositories\nmkdir -p ${repositories}/{super,sub}\ngit -C ${repositories}/super init --bare\ngit -C ${repositories}/sub init --bare\n\n# Create original submodule repository contents\nmkdir -p ${temp}/sub\npushd ${temp}/sub\ngit init .\ndate > submodule-file\ngit add submodule-file\ngit commit -m \"Initial submodule commit\"\ngit remote add origin ${remote}/sub\ngit push -u origin\npopd\nrm -rf ${temp}/sub\n\n# Create original super repository contents\nworkspace=${temp}/workspace\nmkdir -p ${workspace}\npushd ${workspace}\ngit clone ${remote}/super orig\npushd orig\ndate > file\ngit add file\ngit commit -m \"Initial supermodule commit\"\ngit submodule add ${remote}/sub submodule\ngit commit -m \"Add initial submodule\"\ngit push\npopd\n\n# Create a new clone of the super and submodules\ngit clone --recurse-submodules ${remote}/super clone\n\n# Now create a dangling submodule commit in the original supermodule\ndate >> ${workspace}/orig/submodule/submodule-file\ngit -C ${workspace}/orig/submodule add submodule-file\ngit -C ${workspace}/orig/submodule commit -m \"Modify submodule\"\ngit -C ${workspace}/orig checkout -b repro-branch\ngit -C ${workspace}/orig add submodule\ngit -C ${workspace}/orig commit -m \"Bump submodule\"\ngit -C ${workspace}/orig push origin repro-branch\n\n# Now update the second clone to show an error even though the\n# missing submodule commit is on a completely different branch.\ngit -C ${workspace}/clone fetch\n"}]}