Skip to content

panic: index out of range in checkDiffFile when PR contains a mode-only file change #222

Description

@ktersius

Summary

The action panics with index out of range [1] with length 1 at diff/diff.go:44 when a pull request contains a file whose only change is a permission/mode bit (no content diff). The bug is present in both v2.0.0 and v2.1.0.

A fix with a regression test is available in PR #221.


How to reproduce

Open a pull request that includes at least one file with a mode-only change — for example removing the executable bit from a file with no content changes:

git checkout -b repro/mode-change
chmod -x some-file.yaml        # was 100755, becomes 100644
git add some-file.yaml
git commit -m "chore: remove execute bit"
git push origin repro/mode-change
# open PR — the action panics during "Preprocessing diffs..."

The resulting diff contains a block with no --- / +++ file headers:

diff --git a/some-file.yaml b/some-file.yaml
old mode 100755
new mode 100644

Observed behaviour

panic: runtime error: index out of range [1] with length 1

goroutine 1 [running]:
github.com/launchdarkly/find-code-references-in-pull-request/diff.checkDiffFile(...)
        /app/diff/diff.go:44 +0x46a
github.com/launchdarkly/find-code-references-in-pull-request/diff.PreprocessDiffs(...)
        /app/diff/diff.go:21 +0x9d
main.main()
        /app/main.go:60 +0x4c5

Note: a stat: no such file or directory line is sometimes printed just before the panic — that comes from a separate, preceding file processed normally and is unrelated to the crash.


Root cause

checkDiffFile splits OrigName and NewName on / and unconditionally accesses index [1]:

// diff/diff.go:42-45
parsedFileA := strings.SplitN(parsedDiff.OrigName, "/", 2)
parsedFileB := strings.SplitN(parsedDiff.NewName, "/", 2)
fullPathToA := workspace + "/" + parsedFileA[1]   // line 44 — PANICS
fullPathToB := workspace + "/" + parsedFileB[1]

For a standard git diff, file names always carry an a/ or b/ prefix (or are /dev/null), so SplitN always returns two elements. Mode-only diffs have no --- / +++ headers at all. The sourcegraph/go-diff parser therefore returns OrigName = "" and NewName = "" for those entries:

strings.SplitN("", "/", 2)  // returns [""] — length 1, not 2

Accessing [1] on a single-element slice is a runtime panic.


Fix

PR #221 adds a length guard before indexing and a regression test:

if len(parsedFileA) < 2 || len(parsedFileB) < 2 {
    return "", true  // no file headers → nothing to scan, skip entry
}

Environment

Action version v2.1.0 (30f4c4ab) and v2.0.0
Runner OS Linux (self-hosted, x64)
go-diff github.com/sourcegraph/go-diff v0.6.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions