matryer/xbar · error

time.Parse: created_at

Error message

time.Parse: created_at

What it means

This error wraps a failure to parse the GitHub release's `created_at` timestamp. The updater fetches release metadata as JSON (where the timestamp is a raw string field `CreatedAtString`) and then parses it with time.Parse(time.RFC3339Nano, ...). If the GitHub API returns a timestamp in a different format, an empty string, or malformed JSON content in that field, parsing fails and the error is wrapped as 'time.Parse: created_at'.

Source

Thrown at pkg/update/update.go:134

	if err != nil {
		return nil, errors.Wrap(err, "get latest release")
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, errors.Errorf("failed to check for updates: got %s", resp.Status)
	}
	b, err := io.ReadAll(io.LimitReader(resp.Body, u.DownloadBytesLimit))
	if err != nil {
		return nil, errors.Wrap(err, "read body")
	}
	var latestRelease Release
	err = json.Unmarshal(b, &latestRelease)
	if err != nil {
		return nil, errors.Wrap(err, "marshal")
	}
	latestRelease.CreatedAt, err = time.Parse(time.RFC3339Nano, latestRelease.CreatedAtString)
	if err != nil {
		return nil, errors.Wrap(err, "time.Parse: created_at")
	}
	return &latestRelease, nil
}

// HasUpdate checks whether there's an update or not.
func (u *Updater) HasUpdate() (*Release, bool, error) {
	latest, err := u.getLatestRelease()
	if err != nil {
		return nil, false, err
	}
	hasUpdate := hasUpdate(u.CurrentVersion, latest.TagName)
	return latest, hasUpdate, nil
}

// hasUpdate compares the current and latest version strings to
// see if there is an update.
// Returns false if the versions match.
// Returns false is current is in front of latest.

View on GitHub (pinned to d624239058)

Solutions

  1. Verify the endpoint returns real GitHub release JSON with a valid RFC3339 created_at (curl the /releases/latest URL and inspect created_at).
  2. If using a proxy or mirror, normalize the created_at field to RFC3339/RFC3339Nano before the updater reads it.
  3. Ensure your client/auth is not causing GitHub to return an error page or stub body instead of release JSON.
  4. As a fallback, patch the parsing call site to tolerate missing timestamps (leave CreatedAt zero-valued instead of failing).

Example fix

// before
latestRelease.CreatedAt, err = time.Parse(time.RFC3339Nano, latestRelease.CreatedAtString)
if err != nil {
	return nil, errors.Wrap(err, "time.Parse: created_at")
}
// after
if latestRelease.CreatedAtString != "" {
	t, perr := time.Parse(time.RFC3339Nano, latestRelease.CreatedAtString)
	if perr == nil {
		latestRelease.CreatedAt = t
	}
}
Defensive patterns

Strategy: validation

Validate before calling

if rel.CreatedAtString == "" || rel.CreatedAtString == "null" {
	return errors.New("release has no created_at timestamp")
}
if _, err := time.Parse(time.RFC3339Nano, rel.CreatedAtString); err != nil {
	return fmt.Errorf("bad created_at %q: %w", rel.CreatedAtString, err)
}

Type guard

func validRFC3339(s string) bool {
	_, err := time.Parse(time.RFC3339Nano, s)
	return err == nil
}

Try / catch

rel, err := u.HasUpdate()
if err != nil {
	if strings.Contains(err.Error(), "time.Parse: created_at") {
		log.Printf("ignoring bad release timestamp: %v", err)
		return nil
	}
	return err
}

Prevention

When it happens

Trigger: Calling Update() or HasUpdate() when the release JSON returned from the GitHub API contains a created_at value that is not valid RFC3339Nano (e.g. empty string, 'null', or a non-RFC3339 date format from a proxy/mirror of the GitHub API).

Common situations: Hitting a self-hosted or rate-limited/proxied GitHub API endpoint that returns timestamps in a different layout; a release created via API with a manually-set malformed created_at; network middleware returning stub/empty JSON bodies (e.g. mocks in tests without created_at set).

Related errors


AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02). Data as JSON: /api/errors/bd6e75ade437f8e3. Report an issue: GitHub.