cloudflare/cloudflared · error
Unable to marshal tunnel credentials to JSON
Error message
Unable to marshal tunnel credentials to JSON
What it means
writeTunnelCredentials marshals the tunnel's credentials struct to JSON before writing it to <tunnelID>.json with 0400 permissions. json.Marshal on this struct should never fail in practice, so this error indicates an internal serialization problem (e.g. an unsupported or corrupt field value in the credentials object).
Source
Thrown at cmd/cloudflared/tunnel/subcommands.go:310
func tunnelFilePath(tunnelID uuid.UUID, directory string) (string, error) {
fileName := fmt.Sprintf("%v.json", tunnelID)
filePath := filepath.Clean(fmt.Sprintf("%s/%s", directory, fileName))
return homedir.Expand(filePath)
}
// writeTunnelCredentials saves `credentials` as a JSON into `filePath`, only if
// the file does not exist already
func writeTunnelCredentials(filePath string, credentials *connection.Credentials) error {
if _, err := os.Stat(filePath); !os.IsNotExist(err) {
if err == nil {
return fmt.Errorf("%s already exists", filePath)
}
return err
}
body, err := json.Marshal(credentials)
if err != nil {
return errors.Wrap(err, "Unable to marshal tunnel credentials to JSON")
}
return os.WriteFile(filePath, body, 0400)
}
func buildListCommand() *cli.Command {
return &cli.Command{
Name: "list",
Action: cliutil.ConfiguredAction(listCommand),
Usage: "List existing tunnels",
UsageText: "cloudflared tunnel [tunnel command options] list [subcommand options]",
Description: "cloudflared tunnel list will display all active tunnels, their created time and associated connections. Use -d flag to include deleted tunnels. See the list of options to filter the list",
Flags: []cli.Flag{
outputFormatFlag,
showDeletedFlag,
listNameFlag,
listNamePrefixFlag,
listExcludeNamePrefixFlag,
listExistedAtFlag,View on GitHub (pinned to 2253eeeb25)
Solutions
- Read the wrapped json.Marshal error for the exact field that failed to serialize
- Update cloudflared to the latest release; this indicates a bug in the version you are running
- If running a custom patch, remove non-JSON-serializable fields from the tunnel credentials struct
- Retry the create/token command to rule out transient corrupted state
Example fix
// before (custom build)
type Credentials struct { Conn func() } // unsupported type
// after
type Credentials struct { AccountTag, TunnelSecret, TunnelID string } Defensive patterns
Strategy: type-guard
Validate before calling
func credentialsSerializable(c Credentials) error {
v := reflect.ValueOf(c)
for i := 0; i < v.NumField(); i++ {
if !v.Field(i).CanInterface() { continue }
switch v.Field(i).Kind() {
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
return fmt.Errorf("field %s not JSON-serializable", v.Type().Field(i).Name)
}
}
return nil
} Type guard
func isMarshalable(v any) bool {
_, err := json.Marshal(v)
return err == nil
} Try / catch
body, err := json.Marshal(credentials)
if err != nil {
return fmt.Errorf("Unable to marshal tunnel credentials to JSON: %w", err)
} Prevention
- Keep the Credentials struct limited to string fields
- Add a unit test marshaling the credentials struct on every struct change
- Pin and update cloudflared versions; treat this error as a bug report
- Validate credentials round-trip (marshal then unmarshal) in CI
When it happens
Trigger: Called from create and tokenCommand when the Credentials struct returned by the tunnel-creation API or token parsing contains a value Go's encoding/json cannot marshal (nominally only for unsupported types like channels, funcs, or cyclic data).
Common situations: Practically rare — it can surface after code modifications introducing a non-serializable field to the credentials struct, or from corrupted in-memory credential state in custom builds/patches of cloudflared.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- failed to serialize json body
- failed to marshal signPayload
- You must supply at least 2 arguments, first the network you
- You must supply exactly one argument, an IP whose route will
- You must supply at least 1 argument, the name of the virtual
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/f30cec4dc9544d46.
Report an issue: GitHub.