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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions