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
- 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".
- Ensure no extra '/' characters appear anywhere in the value; node names must not contain slashes.
- 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.
- 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
- Always write volume tags in "<myname>/<allnames>" format with exactly one '/'.
- Never include '/' inside node names.
- Verify tags exist after volume provisioning completes before reading them.
- Re-create or re-tag volumes created by older/incompatible kops versions.
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
- invalid EtcdClusterSpec (member not found in all nodes): %q
- error loading private key %q: %v
- unable to unmarshall content from template: %s, error: %s
- parsing additional containerd config entry %q: %w
- error parsing path for etcd manifest %s: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/9638e735932eeb4a.
Report an issue: GitHub.