adopt-project.py _default_branch reads the source's branch name through git_out, which decodes strictly, so a source whose HEAD (or origin/HEAD) names a branch that is not UTF-8 ends plan, check and execute in a UnicodeDecodeError traceback, exit 1, from Source.open, with no plan written and no refusal naming the branch #425
Description
(lane openRepoShape-2, 2026-10-12T17:26Z)
Found by this lane's review of PR #353 (the fix for #292) at dcbc37b, its follow-up F1: #292 names _elector and _remote_repository and says whether the other git_out callers need the same is "a separate question". This is that question for the branch name. The same on main, so PR #353 neither causes it nor changes it. Reproduced on origin/main 92e29d9 (git 2.43.0, Python 3.12.3), local only, a stub gh. Not claimed.
What is wrong. Source.open asks the source which branch to adopt through _default_branch (adopt-project.py:865, called at :758): git symbolic-ref --short refs/remotes/origin/HEAD, then git symbolic-ref --short HEAD, each through git_out, which decodes the output strictly (scripts/repo_shape.py:843) and raises Refusal only for a git exit code. The loop catches Refusal (:871), not the UnicodeDecodeError a branch name that is not UTF-8 raises (git branch caf<e9> is legal; a name typed in a Latin-1 terminal is such a name). So a source whose HEAD is on such a branch, or whose origin/HEAD points at one, ends plan, and check and execute through the same Source.open (Plan.open_source, :2026), in a traceback, exit 1, naming neither the branch nor a flag that avoids it. No plan is written and, for execute, no leg repository exists.
# $U: a checkout of openRepoShape at 92e29d9; git-filter-repo on PATH. Local; no network.
git init -q -b "$(printf 'caf\xe9')" "$SRC"; ...three files, one commit...
python3 "$U/adopt-project.py" plan --source "$SRC" --project Northwind --org testorg --elected-by 'T H' --elected-on 2026-09-02 --out "$P"
# exit 1, no plan file
# Traceback: adopt-project.py line 870, in _default_branch -> scripts/repo_shape.py line 843, in git_out
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 3: invalid continuation byte| the source | command | result |
|---|---|---|
HEAD on branch caf<e9> |
plan |
exit 1, UnicodeDecodeError from _default_branch (:870), no plan file |
refs/remotes/origin/HEAD -> origin/caf<e9> |
plan |
exit 1, the same |
a plan written on main, then the source's branch renamed to caf<e9> |
check |
exit 1, the same |
| the same | execute --yes --local-remote-dir |
exit 1, the same; the remote directory holds nothing |
HEAD on main (control) |
plan |
exit 0 |
What it should do. Read the branch as bytes (binary=True) and either refuse by name before any plan is written (exit 2, a code such as source-branch-not-utf8 that says the branch name is not text this tool can put in a plan and what to do: check out a branch whose name is, or name the branch some other way) or fall back to main as the loop already does for an unreadable symbolic ref. The refusal is the safer: a plan that records branch: main for a source whose HEAD is somewhere else adopts the wrong commit. check and execute need the same answer from the same function, so one fix serves the three commands. Do NOT change git_out: scripts/repo_shape.py is a pinned copy (COPIED_FROM_SHAPE), so a change there moves the sha every project's contracts/shape-pin.yaml records for it and every project already cut sees upstream-changed (#292 makes the point).
How to test. In tests/test_adopt_plan.py (or beside PR #353's tests/test_adopt_git_reads.py), with a source made by make_source_repo and a branch named with b"\xe9" (POSIX only; skip where the filesystem or git branch rejects the byte): plan exits 2 with the new code and writes nothing; check and execute on a plan written earlier refuse the same way and execute creates no leg; origin/HEAD pointing at such a name behaves the same; a source on main still plans.
Not in this issue. user.name and the origin URL (#292, PR #353); a branch name that git refuses (the adopt tool's tracking_branch/adopt_branch, tracked separately).
Related: #292 and PR #353 (the neighbouring reads; its "Left out" and review name this one); #385 (the origin URL read by the Triad advisory and the doctor).
Lane: openreposhape-2 (openRepoShape-2)
Activity
brettheap commentedon Oct 16, 2026
CLAIMED — lane openRepoShape-1, session 300f8f46-004a-4e72-8587-4f192be2e108@Eagle, 2026-10-16T11:29:07Z, for #425
Logged in
lanes/log/openRepoShape-1.mdat63de1d129d55529986fcc8676017a05c73629084.The three reads (lane-collision-protocol Rule 1):
adopt-project.py: `Source.open` hands `git rev-parse` the default bra…
tests/test_adopt_c_locale_bytes.py: `Source.open`'s `git rev-parse` i…
family.py: `add`'s refusal of a member's own gitlink held only under …
bootstrap.py: step (a) decodes git's output as the system decodes a n…
adopt-project.py: `_default_branch` reads `origin/HEAD` and HEAD by f…