wtfutil/wtf · error

failed to parse issue %s: %v

Error message

failed to parse issue %s: %v

What it means

getIssueByID fails with this error when the individual issue fetched from Jira cannot be parsed into the Issue struct via utils.ParseJSON. The issue ID is included so the failing record is identifiable. Like error 78, the HTTP request succeeded but the payload doesn't fit the expected struct.

Source

Thrown at modules/jira/client.go:273

			continue
		}
		searchResult.Issues = append(searchResult.Issues, *fullIssue)
	}

	return searchResult, nil
} // getIssueByID fetches full issue details by ID
func (widget *Widget) getIssueByID(issueID string) (*Issue, error) {
	url := fmt.Sprintf("/rest/api/3/issue/%s", issueID)

	resp, err := widget.jiraRequest(url)
	if err != nil {
		return nil, err
	}

	issue := &Issue{}
	err = utils.ParseJSON(issue, bytes.NewReader(resp))
	if err != nil {
		return nil, fmt.Errorf("failed to parse issue %s: %v", issueID, err)
	}

	return issue, nil
}

func buildJql(key string, value string) string {
	return fmt.Sprintf("%s = \"%s\"", key, value)
}

/* -------------------- Unexported Functions -------------------- */

func (widget *Widget) jiraRequest(path string) ([]byte, error) {
	url := fmt.Sprintf("%s%s", widget.settings.domain, path)

	req, err := http.NewRequest("GET", url, http.NoBody)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to bb838c1ccb)

Solutions

  1. Log the raw body for the failing issue ID to see the mismatch
  2. Update the Issue struct's JSON tags to match the current Jira API v3 issue payload
  3. Make brittle fields nullable/omitempty and tolerate nulls in parsing
  4. Check whether an intermediary (proxy/SSO) is rewriting the response

Example fix

// before
type Issue struct {
    Assignee struct{ Name string `json:"name"` } `json:"assignee"` // removed field
}
// after
type Issue struct {
    Assignee *struct {
        AccountID string `json:"accountId"`
    } `json:"assignee"` // nullable pointer, current field
}
Defensive patterns

Strategy: type-guard

Type guard

func looksLikeIssue(body []byte) bool {
    var probe struct {
        ID   string          `json:"id"`
        Key  string          `json:"key"`
        Fields json.RawMessage `json:"fields"`
    }
    return json.Unmarshal(body, &probe) == nil && probe.Key != ""
}

Try / catch

issue, err := widget.getIssueByID(ctx, issueID)
if err != nil {
    if strings.Contains(err.Error(), "failed to parse issue") {
        log.Printf("issue %s payload not parseable - check schema/custom fields: %v", issueID, err)
    }
    return err
}

Prevention

When it happens

Trigger: The issue endpoint returns a body whose fields don't match the Issue struct (missing/renamed keys, unexpected types), or an HTML/error page is returned instead of issue JSON.

Common situations: Jira issue containing custom fields with unexpected shapes, deprecated field names after a Jira Cloud API update, proxy/SSO page injection, issues with null values where the struct expects objects.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03). Data as JSON: /api/errors/8da27449986362f0. Report an issue: GitHub.