kubernetes/kops · error

marshaling cluster config: %w

Error message

marshaling cluster config: %w

What it means

Returned by HCloudClusterConfig when the JSON encoder fails to serialize the Hetzner Cloud ClusterConfig struct. json.Encoder.Encode only fails if the config contains a value that cannot be marshaled (e.g. an unsupported type like chan or func, or a custom MarshalJSON returning an error). The error is wrapped so the underlying marshal cause is preserved.

Source

Thrown at upup/pkg/fi/cloudup/template_functions.go:1250

			CloudInit:    string(userDataBytes),
			Labels:       nodeLabels,
			ServerLabels: serverLabels,
			Taints:       taints,
			// Map the node group's image to both arches, since the autoscaler resolves the arch.
			ImagesForArch: map[string]string{
				"amd64": ig.Spec.Image,
				"arm64": ig.Spec.Image,
			},
		}
	}

	// Use an encoder with HTML escaping disabled so the embedded cloud-init script stays readable.
	var buf bytes.Buffer
	enc := json.NewEncoder(&buf)
	enc.SetEscapeHTML(false)
	enc.SetIndent("", "  ")
	if err := enc.Encode(config); err != nil {
		return "", fmt.Errorf("marshaling cluster config: %w", err)
	}

	// Strip the trailing newline that json.Encoder.Encode appends.
	return strings.TrimRight(buf.String(), "\n"), nil
}

// HCloudSSHKey returns HCLOUD_SSH_KEY as the first SSH key ID.
func (tf *TemplateFunctions) HCloudSSHKey() (string, error) {
	tasks, err := tf.TasksByType("SSHKey")
	if err != nil {
		return "", fmt.Errorf("listing SSH key tasks: %w", err)
	}
	if len(tasks) == 0 {
		return "", nil
	}

	// Use the first SSH key, since the autoscaler accepts a single HCLOUD_SSH_KEY.
	sshKey, ok := tasks[0].(*hetznertasks.SSHKey)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Read the wrapped cause (%w) to identify the field/type that failed to marshal
  2. Inspect the ClusterConfig built by HCloudClusterConfig for newly added or non-standard field types
  3. Ensure any custom MarshalJSON implementations on config types handle nil receivers and edge cases
  4. If a field cannot be JSON-encoded, change its type or add an explicit MarshalJSON

Example fix

// before: config contains an unsupported field type
resp, err := tf.HCloudClusterConfig()
// after: guard against marshal failure and surface the underlying cause
if err != nil {
	return fmt.Errorf("hcloud cluster config: %w", err) // cause names the bad field
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure config only contains JSON-encodable types before calling
b, err := json.Marshal(config)
if err != nil { /* fix offending field before rendering */ }

Type guard

func isJSONEncodable(v interface{}) bool { var b []byte; _, err := json.Marshal(v); return err == nil }

Try / catch

cfg, err := tf.HCloudClusterConfig()
if err != nil {
	return fmt.Errorf("hcloud cluster config unavailable: %w", err)
}

Prevention

When it happens

Trigger: Calling HCloudClusterConfig (directly or via HCloudClusterConfigChecksum) when the assembled ClusterConfig contains a field of an unencodable type or a custom marshaler that errors.

Common situations: A new field added to the Hetzner cluster config struct has a type json cannot encode; a struct field implements json.Marshaler and returns an error; bad upstream data from cluster spec parsing produced an invalid nested value.

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


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/c477310aea5dcc25. Report an issue: GitHub.