cloudflare/cloudflared · error

metadata JWT is more than

Error message

metadata JWT is more than %s in the future

What it means

validateMetadataIssuedAt also rejects JWTs whose `iat` is in the future by more than metadataAllowedClockSkew. A metadata token claiming to be issued significantly after the current time is treated as invalid, protecting against tokens minted with bad clocks or forged timestamps.

Solutions

  1. Synchronize the local clock with NTP (chronyd/systemd-timesyncd, `w32tm /resync` on Windows).
  2. For VMs/containers, enable the hypervisor/host clock-sync feature and resume-pause clock catch-up.
  3. Re-run the command after the clock is correct; the token itself is fine.
  4. Verify no test/mocked time source (e.g. faketime, frozen CLOCK_REALTIME) is in effect for the process.

Example fix

// before
$ cloudflared access login https://myteam.cloudflareaccess.com
// error: metadata JWT is more than 5m0s in the future
// after — fix local clock, then retry
$ sudo timedatectl set-ntp true && sudo chronyc makestep
$ cloudflared access login https://myteam.cloudflareaccess.com
Defensive patterns

Strategy: validation

Validate before calling

// detect a lagging local clock before calling GetAppInfo
resp, err := http.Head("https://www.cloudflare.com")
if err == nil {
    serverDate, _ := http.ParseTime(resp.Header.Get("Date"))
    if skew := time.Since(serverDate); skew < -time.Minute {
        return fmt.Errorf("local clock is %s behind server time; enable NTP", -skew)
    }
}

Try / catch

appInfo, err := token.GetAppInfo(authDomain, appURL)
if err != nil {
    if strings.Contains(err.Error(), "in the future") {
        return fmt.Errorf("%w — local clock is behind; resynchronize with NTP and retry", err)
    }
    return err
}

Prevention

When it happens

Trigger: GetAppInfo parses a metadata JWT whose `iat` exceeds now + metadataAllowedClockSkew. Typically caused by the local machine's clock running behind real time (slow clock) while the edge issued the token with the correct current timestamp.

Common situations: Laptop/desktop with drifting RTC or paused VM whose clock fell behind; container with wrong timezone/clock after host suspend; NTP not running so local time lags the Cloudflare edge by minutes or hours.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/63f037e95c13604c. Report an issue: GitHub.

Appendix: source

Thrown at token/token.go:516

	_ = resp.Body.Close()

	rawJWT := resp.Header.Get(accessMetadataRespHeader)
	if rawJWT == "" {
		return "", fmt.Errorf("failed to find Access application at %s", reqURL)
	}
	return rawJWT, nil
}

func validateMetadataIssuedAt(iat int64, now time.Time) error {
	if iat <= 0 {
		return errors.New("metadata JWT iat is missing or invalid")
	}
	issuedAt := time.Unix(iat, 0)
	if issuedAt.Before(now.Add(-metadataMaxAge)) {
		return fmt.Errorf("metadata JWT is older than %s", metadataMaxAge)
	}
	if issuedAt.After(now.Add(metadataAllowedClockSkew)) {
		return fmt.Errorf("metadata JWT is more than %s in the future", metadataAllowedClockSkew)
	}
	return nil
}

func handleRedirects(req *http.Request, via []*http.Request, orgToken string) error {
	// attach org token to login request
	if strings.Contains(req.URL.Path, AccessLoginWorkerPath) {
		req.AddCookie(&http.Cookie{Name: tokenCookie, Value: orgToken}) //nolint: gosec
	}

	// attach app session cookie to authorized request
	if strings.Contains(req.URL.Path, AccessAuthorizedWorkerPath) {
		// We need to check and see if the CF_APP_SESSION cookie was set
		for _, prevReq := range via {
			if prevReq != nil && prevReq.Response != nil {
				for _, c := range prevReq.Response.Cookies() {
					if c.Name == appSessionCookie {
						req.AddCookie(&http.Cookie{Name: appSessionCookie, Value: c.Value}) //nolint: gosec

View on GitHub (pinned to 2253eeeb25)