gastownhall/beads · error

missing source path for porcelain rename/copy record %q

Error message

missing source path for porcelain rename/copy record %q

What it means

In porcelain -z output, rename (R) and copy (C) records are followed by a second NUL-separated field: the original (source) path. bd consumes that next record to include both endpoints in the fingerprint; this error means the expected source path was absent or empty, so the status stream is truncated or malformed.

Source

Thrown at cmd/bd/worktree_cmd.go:1137

}

func fingerprintWorktreeStatusPaths(worktreePath, status string) (string, error) {
	records := strings.Split(status, "\x00")
	pathSet := make(map[string]struct{})
	for index := 0; index < len(records); index++ {
		record := records[index]
		if record == "" {
			continue
		}
		if len(record) < 4 || record[2] != ' ' {
			return "", fmt.Errorf("unexpected porcelain status record %q", record)
		}
		code := record[:2]
		pathSet[record[3:]] = struct{}{}
		if strings.ContainsAny(code, "RC") {
			index++
			if index >= len(records) || records[index] == "" {
				return "", fmt.Errorf("missing source path for porcelain rename/copy record %q", record)
			}
			pathSet[records[index]] = struct{}{}
		}
	}

	paths := make([]string, 0, len(pathSet))
	for path := range pathSet {
		paths = append(paths, path)
	}
	sort.Strings(paths)

	hasher := sha256.New()
	for _, gitPath := range paths {
		cleanPath := filepath.Clean(filepath.FromSlash(gitPath))
		if cleanPath == "." ||
			filepath.IsAbs(cleanPath) ||
			cleanPath == ".." ||
			strings.HasPrefix(cleanPath, ".."+string(filepath.Separator)) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Regenerate status directly from git to confirm output is complete: `git -C <worktree> status --porcelain=v1 -z --untracked-files=all --ignored=matching`
  2. Check for R/C entries (`git status --porcelain=v1`) and verify each has a preceding `->` source in non-z form; upgrade git if output looks truncated
  3. If this comes from a test/mock, fix the fixture to include the NUL-separated source path after each R/C record

Example fix

// before (bad fixture)
"R  new.txt\x00"
// after
"R  new.txt\x00old.txt\x00"
Defensive patterns

Strategy: validation

Validate before calling

records := strings.Split(status, "\x00")
for i, r := range records {
    if len(r) > 2 && strings.ContainsAny(r[:2], "RC") {
        if i+1 >= len(records) || records[i+1] == "" {
            return fmt.Errorf("fixture/truncated output: R/C record %q lacks source path", r)
        }
    }
}

Prevention

When it happens

Trigger: fingerprintWorktreeStatusPaths sees an R/C status code but the next NUL-separated record is empty or missing — truncated `git status` output or a caller passing a hand-built/partial status string.

Common situations: Renamed files in the index combined with output truncation; testing/mocking code that fabricates porcelain records without the rename source; a git build emitting non-standard rename serialization.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/b90b668e3f61093d. Report an issue: GitHub.