gastownhall/beads · error

parse Jira timestamp: %w

Error message

parse Jira timestamp: %w

What it means

FetchIssueTimestamp wraps errors from ParseTimestamp on the issue's "updated" field. It means the JSON parsed fine but the timestamp string (or its absence, yielding "") could not be parsed into a time.Time in the expected Jira format (e.g. 2006-01-02T15:04:05.000-0700). The library throws it because it cannot return a usable sync timestamp from an empty or malformed date.

Source

Thrown at internal/jira/client.go:156

	body, err := c.doRequest(ctx, "GET", apiURL, nil)
	if err != nil {
		return zero, fmt.Errorf("fetch issue %s: %w", jiraKey, err)
	}

	var result struct {
		Fields struct {
			Updated string `json:"updated"`
		} `json:"fields"`
	}

	if err := json.Unmarshal(body, &result); err != nil {
		return zero, fmt.Errorf("parse Jira response: %w", err)
	}

	updated, err := ParseTimestamp(result.Fields.Updated)
	if err != nil {
		return zero, fmt.Errorf("parse Jira timestamp: %w", err)
	}

	return updated, nil
}

// searchFields is the default set of fields to request in search/get queries.
const searchFields = "summary,description,status,priority,issuetype,project,assignee,labels,created,updated,resolution"

// SearchIssues queries Jira using JQL and returns all matching issues, handling pagination.
func (c *Client) SearchIssues(ctx context.Context, jql string) ([]Issue, error) {
	var allIssues []Issue
	startAt := 0
	nextPageToken := ""
	maxResults := 100
	page := 0
	useV2Pagination := c.APIVersion == "2"

	for {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the issue key is correct and the account can see the issue's updated field.
  2. Log result.Fields.Updated before parsing to inspect the actual value.
  3. Check ParseTimestamp's accepted layout and extend it if your server emits a different RFC format.
  4. Confirm the requested fields=updated parameter is honored by your Jira version.

Example fix

// before
updated, err := ParseTimestamp(result.Fields.Updated)
if err != nil {
    return zero, fmt.Errorf("parse Jira timestamp: %w", err)
}
// after: distinguish empty timestamp from malformed
if result.Fields.Updated == "" {
    return zero, fmt.Errorf("issue %s has no updated timestamp", jiraKey)
}
updated, err := ParseTimestamp(result.Fields.Updated)
if err != nil {
    return zero, fmt.Errorf("parse Jira timestamp %q: %w", result.Fields.Updated, err)
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the key format before calling:
var jiraKeyRe = regexp.MustCompile(`^[A-Z][A-Z0-9]*-\d+$`)
func validJiraKey(k string) bool { return jiraKeyRe.MatchString(k) }

Type guard

func hasUpdated(fieldsJSON []byte) bool {
    var r struct{ Fields struct{ Updated string `json:"updated"` } `json:"fields"` }
    if json.Unmarshal(fieldsJSON, &r) != nil { return false }
    return r.Fields.Updated != ""
}

Try / catch

ts, err := client.FetchIssueTimestamp(ctx, key)
if err != nil {
    if strings.Contains(err.Error(), "parse Jira timestamp") {
        log.Printf("bad/missing updated timestamp for %s; skipping sync for this issue", key)
        return nil // skip rather than fail the whole sync
    }
    return err
}

Prevention

When it happens

Trigger: Calling FetchIssueTimestamp on an issue whose "updated" field is missing, null, or formatted unexpectedly (e.g. issue key resolves to a non-issue entity, restricted field, or custom date format).

Common situations: Typo'd or nonexistent issue key that still returns a body; field-level permissions hiding "updated"; Jira Cloud/Server format differences (millisecond and timezone handling); deprecated Jira versions emitting different date formats.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/5226f56fc2c1526a. Report an issue: GitHub.