From c8187137f542e02cbc797572c66e1f0a329d2f6e Mon Sep 17 00:00:00 2001 From: Nicolas Suzor Date: Sat, 17 Jan 2026 18:00:56 +1000 Subject: [PATCH] fix(utils): prevent nil pointer panic in ResolvePartialID (#1132) Add nil check at start of ResolvePartialID to return a proper error instead of panicking when storage interface is nil. Root cause: When bd refile (or other commands) is called with a nil storage, calling store.SearchIssues() panics with SIGSEGV. This can happen when routing fails to initialize storage properly. Now returns: "cannot resolve issue ID : storage is nil" Fixes: bd-7ypor Co-authored-by: Claude Opus 4.5 --- internal/utils/id_parser.go | 4 ++++ internal/utils/id_parser_test.go | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/internal/utils/id_parser.go b/internal/utils/id_parser.go index 8d31033a..002e26bf 100644 --- a/internal/utils/id_parser.go +++ b/internal/utils/id_parser.go @@ -37,6 +37,10 @@ func ParseIssueID(input string, prefix string) string { // - No issue found matching the ID // - Multiple issues match (ambiguous prefix) func ResolvePartialID(ctx context.Context, store storage.Storage, input string) (string, error) { + if store == nil { + return "", fmt.Errorf("cannot resolve issue ID %q: storage is nil", input) + } + // Fast path: Use SearchIssues with exact ID filter (GH#942). // This uses the same query path as "bd list --id", ensuring consistency. // Previously we used GetIssue which could fail in cases where SearchIssues diff --git a/internal/utils/id_parser_test.go b/internal/utils/id_parser_test.go index 999a4f0e..cbc7de79 100644 --- a/internal/utils/id_parser_test.go +++ b/internal/utils/id_parser_test.go @@ -316,6 +316,21 @@ func TestResolvePartialID_NoConfig(t *testing.T) { } } +func TestResolvePartialID_NilStorage(t *testing.T) { + ctx := context.Background() + + // Test that nil storage returns an error instead of panicking + _, err := ResolvePartialID(ctx, nil, "bd-123") + if err == nil { + t.Fatal("ResolvePartialID with nil storage should return error, got nil") + } + + expectedMsg := "storage is nil" + if !contains(err.Error(), expectedMsg) { + t.Errorf("ResolvePartialID error = %q; want error containing %q", err.Error(), expectedMsg) + } +} + func TestExtractIssuePrefix(t *testing.T) { tests := []struct { name string