gastownhall/beads · error

empty timestamp

Error message

empty timestamp

What it means

ParseTimestamp rejects an empty string input before attempting any format parsing. Jira timestamps are ISO 8601 strings; an empty value means the upstream JSON field was absent, null, or an empty string, and no meaningful time can be produced. The zero time.Time and this error are returned to all callers (FetchIssueTimestamp, jiraToTrackerIssue, anonymous closures).

Source

Thrown at internal/jira/refs.go:42

	return true
}

// ExtractJiraKey extracts the Jira issue key from an external_ref URL.
// For example, "https://company.atlassian.net/browse/PROJ-123" returns "PROJ-123".
func ExtractJiraKey(externalRef string) string {
	idx := strings.LastIndex(externalRef, "/browse/")
	if idx == -1 {
		return ""
	}
	return externalRef[idx+len("/browse/"):]
}

// ParseTimestamp parses Jira's timestamp format into a time.Time.
// Jira uses ISO 8601 with timezone: 2024-01-15T10:30:00.000+0000 or 2024-01-15T10:30:00.000Z
func ParseTimestamp(ts string) (time.Time, error) {
	if ts == "" {
		return time.Time{}, fmt.Errorf("empty timestamp")
	}

	// Try common formats
	formats := []string{
		"2006-01-02T15:04:05.000-0700",
		"2006-01-02T15:04:05.000Z",
		"2006-01-02T15:04:05-0700",
		"2006-01-02T15:04:05Z",
		time.RFC3339,
		time.RFC3339Nano,
	}

	for _, format := range formats {
		if t, err := time.Parse(format, ts); err == nil {
			return t, nil
		}
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the field is non-empty before calling ParseTimestamp and handle the unset case explicitly.
  2. Default to a sentinel (e.g. time.Time{} or the issue's created timestamp) when the field is legitimately empty.
  3. Inspect the raw Jira JSON response to confirm which field is empty and whether that is expected.
  4. If the field should never be empty, check for Jira instance/plugin issues or API version differences.

Example fix

// before
updated, err := jira.ParseTimestamp(issue.Fields.Updated)
// after
var updated time.Time
if issue.Fields.Updated != "" {
    updated, err = jira.ParseTimestamp(issue.Fields.Updated)
    if err != nil { return err }
} else {
    updated, _ = jira.ParseTimestamp(issue.Fields.Created)
}
Defensive patterns

Strategy: validation

Validate before calling

func safeParseTimestamp(ts string) (time.Time, error) {
    if strings.TrimSpace(ts) == "" {
        return time.Time{}, nil // treat unset as zero-value, not an error
    }
    return jira.ParseTimestamp(ts)
}

Type guard

func hasTimestamp(s string) bool {
    return strings.TrimSpace(s) != ""
}

Try / catch

t, err := jira.ParseTimestamp(ts)
if err != nil {
    if strings.Contains(err.Error(), "empty timestamp") {
        t = time.Time{} // or fall back to Created
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: ParseTimestamp("") is called — i.e. the Jira issue JSON had an empty/missing timestamp field (e.g. updated, created, or a custom field) passed through directly.

Common situations: Jira fields that are legitimately unset (issue never updated in some views, empty custom datetime field); JSON unmarshaling into a string field that was null; older Jira Server versions omitting fields the Cloud API returns; mapping code not checking for null before calling ParseTimestamp.

Related errors


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