kubernetes/kops · error

invalid EtcdClusterSpec (expected two tokens): %q

Error message

invalid EtcdClusterSpec (expected two tokens): %q

What it means

ParseEtcdClusterSpec parses an etcd cluster role tag stored on an etcd volume, expected in the form "<myname>/<allnames>" (e.g. "node1/node1,node2,node3"). This error is thrown when the trimmed value does not split into exactly two '/'-separated tokens. It means the volume tag content is malformed and cannot encode a valid cluster membership spec.

Source

Thrown at pkg/etcd/cluster_spec.go:46

	// NodeName is my nodename in the cluster
	NodeName string `json:"nodeName,omitempty"`
	// NodeNames is a collection of node members in the cluster
	NodeNames []string `json:"nodeNames,omitempty"`
}

// String returns a string representation of the EtcdClusterSpec
func (e *EtcdClusterSpec) String() string {
	return DebugString(e)
}

// ParseEtcdClusterSpec parses a tag on a volume that encodes an etcd cluster role
// The format is "<myname>/<allnames>", e.g. "node1/node1,node2,node3"
func ParseEtcdClusterSpec(clusterKey, v string) (*EtcdClusterSpec, error) {
	v = strings.TrimSpace(v)

	tokens := strings.Split(v, "/")
	if len(tokens) != 2 {
		return nil, fmt.Errorf("invalid EtcdClusterSpec (expected two tokens): %q", v)
	}

	nodeName := tokens[0]
	nodeNames := strings.Split(tokens[1], ",")

	found := false
	for _, s := range nodeNames {
		if s == nodeName {
			found = true
		}
	}
	if !found {
		return nil, fmt.Errorf("invalid EtcdClusterSpec (member not found in all nodes): %q", v)
	}

	c := &EtcdClusterSpec{
		ClusterKey: clusterKey,
		NodeName:   nodeName,

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Print the offending value (quoted in the error) and correct the volume tag to the exact "<myname>/<allnames>" format, e.g. "node1/node1,node2,node3".
  2. Ensure no extra '/' characters appear anywhere in the value; node names must not contain slashes.
  3. If volumes were created by an incompatible kops version, either recreate the etcd volumes with the current `kops` version or use `kops toolbox` tooling to migrate the tags.
  4. Verify the instance/volume was fully initialized — an empty tag means the etcd volume setup did not complete; rerun cluster creation/upgrade.

Example fix

// before: malformed tag value on the etcd volume
v := "node1"
// after: correct two-token format "<myname>/<allnames>"
v := "node1/node1,node2,node3"
Defensive patterns

Strategy: validation

Validate before calling

// validate an etcd cluster-spec tag before calling ParseEtcdClusterSpec
func validEtcdClusterSpecShape(v string) bool {
    v = strings.TrimSpace(v)
    if v == "" {
        return false
    }
    parts := strings.Split(v, "/")
    if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
        return false
    }
    for _, n := range strings.Split(parts[1], ",") {
        if strings.TrimSpace(n) == "" {
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: findEtcdStatus reads the cluster-spec tag from an etcd volume and passes it to ParseEtcdClusterSpec; the string contains zero '/' characters (e.g. "node1"), or more than one '/' (e.g. "node1/node1,node2/extra"), or an empty string, so strings.Split returns a slice whose length != 2.

Common situations: Volumes created by an older kops version with a different tag format; a volume tag manually edited or truncated; passing a hostname or FQDN containing '/' by mistake; an empty/uninitialized tag value on a recreated volume.

Related errors


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