{"record":{"id":"015ed1f385e97566","repo":"cloudflare/cloudflared","slug":"failed-to-marshal-transfer-service-response","errorCode":null,"errorMessage":"failed to marshal transfer service response","messagePattern":"failed to marshal transfer service response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/token.go","lineNumber":402,"sourceCode":"\t}\n\treturn getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, autoClose, isFedramp, log)\n}\n\n// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,\n// and return the app token.\nfunc getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {\n\t// If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow.\n\n\t// this weird parameter is the resource name (token) and the key/value\n\t// we want to send to the transfer service. the key is token and the value\n\t// is blank (basically just the id generated in the transfer service)\n\tresourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, \"\", true, useHostOnly, autoClose, isFedramp, log, appTokenPath+\".url\")\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to run transfer service\")\n\t}\n\tvar resp transferServiceResponse\n\tif err = json.Unmarshal(resourceData, &resp); err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to marshal transfer service response\")\n\t}\n\n\t// If we were able to get the auth domain and generate an org token path, lets write it to disk.\n\tif orgTokenPath != \"\" {\n\t\tif err := os.WriteFile(orgTokenPath, []byte(resp.OrgToken), 0600); err != nil {\n\t\t\treturn \"\", errors.Wrap(err, \"failed to write org token to disk\")\n\t\t}\n\t}\n\n\tif err := os.WriteFile(appTokenPath, []byte(resp.AppToken), 0600); err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to write app token to disk\")\n\t}\n\n\treturn resp.AppToken, nil\n}\n\n// GetAppInfo discovers the Access application protecting reqURL by requesting\n// a signed metadata JWT from the Cloudflare edge. The JWT signature is verified","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/token.go#L384-L420","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// after: surface the raw payload when unmarshal fails\nvar resp transferServiceResponse\nif err = json.Unmarshal(resourceData, &resp); err != nil {\n\treturn \"\", fmt.Errorf(\"failed to marshal transfer service response: %w (body: %.200s)\", err, string(resourceData))\n}","handlingStrategy":"try-catch","validationCode":"// sanity-check the transfer service returns parseable JSON before use\nif !json.Valid(resourceData) {\n\treturn fmt.Errorf(\"transfer service returned non-JSON body: %.200s\", string(resourceData))\n}","typeGuard":null,"tryCatchPattern":"token, err := FetchToken(...)\nif err != nil && strings.Contains(err.Error(), \"failed to marshal transfer service response\") {\n\t// capture raw payload and version for a bug report / schema mismatch diagnosis\n\tlog.Error().Str(\"cloudflaredVersion\", version).Msg(\"transfer service response unparseable; check for proxy HTML or version mismatch\")\n}","preventionTips":["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"],"tags":["json","transfer-service","api-response","version-mismatch"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}