cloudflare/cloudflared · error

failed to run transfer service

Error message

failed to run transfer service

What it means

getTokensFromEdge wraps this error when RunTransfer — the local transfer-service flow that mints a token by relaying through a browser/localhost transfer — returns an error. This is the fallback path used when no cached app or org token can be found or exchanged, so it commonly surfaces as an interactive login failure.

Source

Thrown at token/token.go:398

				return "", errors.Wrap(err, "failed to write app token to disk")
			}
			return appToken, nil
		}
	}
	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

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Run `cloudflared access login <url>` interactively once on a machine with a browser to seed the org token, then copy it to the headless host
  2. Check that the localhost port the transfer service uses is free and not blocked by firewall/proxy
  3. Inspect the wrapped RunTransfer error for the concrete failure (port bind, edge HTTP error, browser launch)
  4. Verify the service can reach the Cloudflare edge (no egress restrictions on the access domain)

Example fix

// headless: reuse an org token minted on an interactive machine
scp interactive:~/.cloudflared/org-token-* headless:~/.cloudflared/
# then re-run cloudflared access; it will exchange the org token instead of running the transfer service
Defensive patterns

Strategy: fallback

Validate before calling

// headless preflight: can we reach the edge and is an org token present?
toks, _ := filepath.Glob(homeDir + "/.cloudflared/org-token-*")
if len(toks) == 0 && !isInteractive() {
	return fmt.Errorf("no org token cached and no browser available — run 'cloudflared access login' on an interactive host")
}

Try / catch

token, err := FetchToken(...)
if err != nil && strings.Contains(err.Error(), "failed to run transfer service") {
	// interactive flow unavailable: guide user to manual login
	return fmt.Errorf("%w — run 'cloudflared access login <url>' once to authenticate", err)
}

Prevention

When it happens

Trigger: getToken finds no usable cached tokens and calls getTokensFromEdge -> RunTransfer, which fails because the local transfer service cannot start (port binding failure), the browser login flow fails, or the edge returns an error during the transfer.

Common situations: Headless environments (CI, SSH-only servers) where the interactive browser flow cannot complete; localhost port conflicts blocking the transfer service; corporate proxies blocking the loopback or edge callbacks; stale/expired org token that cannot be refreshed non-interactively.

Related errors


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