andygrunwald / andygrunwald/go-jira

Nil pointer panic in GetWatchersWithContext when watchers lack AccountID

Open
#743 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
1.6k
Forks
500
PR merge metrics
No merged PRs in 30d

Description

## Description

`IssueService.GetWatchersWithContext()` panics with a nil pointer dereference when processing watchers that don't have an `AccountID` field. This commonly occurs with legacy Jira Server installations or watchers migrated from older Jira versions.

## Version

- **go-jira version**: v1.17.0 (bug also exists in v1.12.0)
- **Go version**: go1.25.4
- **Jira**: Cloud/Server (any version with legacy watchers)

## Steps to Reproduce

1. Call `GetWatchersWithContext()` on an issue that has watchers without `AccountID`
2. Observe panic

### Minimal Reproducible Example

```go
package main

import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"

jira "github.com/andygrunwald/go-jira"
)

func main() {
// Mock server returns watcher without AccountID (legacy format)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/rest/api/2/issue/TEST-123/watchers" {
response := map[string]interface{}{
"self": "https://example.atlassian.net/rest/api/2/issue/TEST-123/watchers",
"watchCount": 1,
"watchers": []map[string]interface{}{
{
"name": "olduser",
"displayName": "Old User",
"active": true,
// No accountId field
},
},
}
json.NewEncoder(w).Encode(response)
return
}
http.NotFound(w, r)
}))
defer server.Close()

client, _ := jira.NewClient(server.Client(), server.URL)

// This panics:
_, _, _ = client.Issue.GetWatchersWithContext(context.Background(), "TEST-123")
}
```

## Expected Behavior

The function should either:
- Skip watchers without `AccountID` gracefully, or
- Return an error, or
- Return the watcher data available (name, displayName)

## Actual Behavior

```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x756eb3]

goroutine 1 [running]:
github.com/andygrunwald/go-jira.(*IssueService).GetWatchersWithContext(...)
.../go-jira@v1.17.0/issue.go:1575
```

## Root Cause

In `issue.go` at line ~1575:

```go
func (s *IssueService) GetWatchersWithContext(ctx context.Context, issueID string) (*[]User, *Response, error) {
// ...
result := []User{}
for _, watcher := range watches.Watchers {
var user *User
if watcher.AccountID != "" {
user, resp, err = s.client.User.GetByAccountID(watcher.AccountID)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
}
result = append(result, *user) // ← PANIC: user is nil if AccountID is empty
}
return &result, resp, nil
}
```

When `watcher.AccountID` is empty, `user` remains `nil`, and dereferencing `*user` causes a panic.

## Suggested Fix

### Option 1: Skip watchers without AccountID

```go
for _, watcher := range watches.Watchers {
if watcher.AccountID == "" {
continue // Skip legacy watchers
}
user, resp, err := s.client.User.GetByAccountID(watcher.AccountID)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
if user != nil {
result = append(result, *user)
}
}
```

### Option 2: Handle both legacy and modern formats

```go
for _, watcher := range watches.Watchers {
var user *User
var err error

if watcher.AccountID != "" {
user, resp, err = s.client.User.GetByAccountID(watcher.AccountID)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
} else if watcher.Name != "" {
// Fallback for legacy watchers - use watcher data directly
user = &User{
Name: watcher.Name,
DisplayName: watcher.DisplayName,
Active: watcher.Active,
}
}

if user != nil {
result = append(result, *user)
}
}
```

## Additional Context

This issue affects environments with:
- Legacy Jira Server installations
- Migrated Jira Cloud instances with old watchers
- Any scenario where watchers were added before AccountID became mandatory

Probably related: https://github.com/andygrunwald/go-jira/issues/272

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.