cloudflare/cloudflared · error

failed to marshal transfer service response

Error message

failed to marshal transfer service response

What it means

getTokensFromEdge wraps this error when the transfer service succeeded and returned resource data, but json.Unmarshal into transferServiceResponse fails — meaning the payload from the edge/transfer service is not the expected JSON shape. The (misleading) message says 'marshal' but the code path is unmarshal of the response body.

Source

Thrown at token/token.go:402

	}
	return getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, autoClose, isFedramp, log)
}

// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,
// and return the app token.
func getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
	// If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow.

	// this weird parameter is the resource name (token) and the key/value
	// we want to send to the transfer service. the key is token and the value
	// is blank (basically just the id generated in the transfer service)
	resourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, "", true, useHostOnly, autoClose, isFedramp, log, appTokenPath+".url")
	if err != nil {
		return "", errors.Wrap(err, "failed to run transfer service")
	}
	var resp transferServiceResponse
	if err = json.Unmarshal(resourceData, &resp); err != nil {
		return "", errors.Wrap(err, "failed to marshal transfer service response")
	}

	// If we were able to get the auth domain and generate an org token path, lets write it to disk.
	if orgTokenPath != "" {
		if err := os.WriteFile(orgTokenPath, []byte(resp.OrgToken), 0600); err != nil {
			return "", errors.Wrap(err, "failed to write org token to disk")
		}
	}

	if err := os.WriteFile(appTokenPath, []byte(resp.AppToken), 0600); err != nil {
		return "", errors.Wrap(err, "failed to write app token to disk")
	}

	return resp.AppToken, nil
}

// GetAppInfo discovers the Access application protecting reqURL by requesting
// a signed metadata JWT from the Cloudflare edge. The JWT signature is verified

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Upgrade cloudflared to the latest version to eliminate client/edge response schema mismatch
  2. Log or print the raw resourceData to see what was actually returned (HTML? empty? error JSON?)
  3. Bypass any proxy for the edge endpoints and retry the transfer flow
  4. If the raw body is an auth error, re-authenticate with `cloudflared access login` and retry

Example fix

// after: surface the raw payload when unmarshal fails
var resp transferServiceResponse
if err = json.Unmarshal(resourceData, &resp); err != nil {
	return "", fmt.Errorf("failed to marshal transfer service response: %w (body: %.200s)", err, string(resourceData))
}
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check the transfer service returns parseable JSON before use
if !json.Valid(resourceData) {
	return fmt.Errorf("transfer service returned non-JSON body: %.200s", string(resourceData))
}

Try / catch

token, err := FetchToken(...)
if err != nil && strings.Contains(err.Error(), "failed to marshal transfer service response") {
	// capture raw payload and version for a bug report / schema mismatch diagnosis
	log.Error().Str("cloudflaredVersion", version).Msg("transfer service response unparseable; check for proxy HTML or version mismatch")
}

Prevention

When it happens

Trigger: RunTransfer returns bytes that are not valid transferServiceResponse JSON: an HTML error page, an empty body, a proxy interstitial, or a cloudflared/edge version mismatch producing a different response schema.

Common situations: Captive portals or proxies injecting HTML into the response; cloudflared version older than what the edge transfer service now returns (schema drift); truncated responses over flaky links; service token/auth errors returning an unexpected error body.

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 cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/015ed1f385e97566. Report an issue: GitHub.