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 verifiedView on GitHub (pinned to 2253eeeb25)
Solutions
- Upgrade cloudflared to the latest version to eliminate client/edge response schema mismatch
- Log or print the raw resourceData to see what was actually returned (HTML? empty? error JSON?)
- Bypass any proxy for the edge endpoints and retry the transfer flow
- 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
- Keep cloudflared on a recent release to match edge response schemas
- Bypass proxies/captive portals for edge endpoints
- Log raw response bodies on unmarshal failure for diagnosis
- Add contract tests against the transfer service in staging
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
- unable to unmarshal LogEventType string
- unable to unmarshal LogEventType
- unable to unmarshal LogLevel string
- backend returned invalid network %s
- The tunnel credentials file should be .json but you gave a .
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/015ed1f385e97566.
Report an issue: GitHub.