VictoriaMetrics/VictoriaMetrics · error
cannot parse nodes: %w
Error message
cannot parse nodes: %w
What it means
parseNodes json.Unmarshals the /nodes response body into []node and wraps unmarshal failures with this error. The HTTP layer succeeded but the payload is not the expected JSON array of Swarm nodes. Typically a proxy-injected body or a schema/type mismatch between the daemon's output and the library's struct.
Source
Thrown at lib/promscrape/discovery/dockerswarm/nodes.go:76
return addNodeLabels(nodes, cfg.port), nil
}
func getNodes(cfg *apiConfig) ([]node, error) {
filtersQueryArg := ""
if cfg.role == "nodes" {
filtersQueryArg = cfg.filtersQueryArg
}
resp, err := cfg.getAPIResponse("/nodes", filtersQueryArg)
if err != nil {
return nil, fmt.Errorf("cannot query dockerswarm api for nodes: %w", err)
}
return parseNodes(resp)
}
func parseNodes(data []byte) ([]node, error) {
var nodes []node
if err := json.Unmarshal(data, &nodes); err != nil {
return nil, fmt.Errorf("cannot parse nodes: %w", err)
}
return nodes, nil
}
func addNodeLabels(nodes []node, port int) []*promutil.Labels {
var ms []*promutil.Labels
for _, node := range nodes {
m := promutil.NewLabels(16)
m.Add("__address__", discoveryutil.JoinHostPort(node.Status.Addr, port))
m.Add("__meta_dockerswarm_node_address", node.Status.Addr)
m.Add("__meta_dockerswarm_node_availability", node.Spec.Availability)
m.Add("__meta_dockerswarm_node_engine_version", node.Description.Engine.EngineVersion)
m.Add("__meta_dockerswarm_node_hostname", node.Description.Hostname)
m.Add("__meta_dockerswarm_node_id", node.ID)
m.Add("__meta_dockerswarm_node_manager_address", node.ManagerStatus.Addr)
m.Add("__meta_dockerswarm_node_manager_leader", fmt.Sprintf("%t", node.ManagerStatus.Leader))
m.Add("__meta_dockerswarm_node_manager_reachability", node.ManagerStatus.Reachability)
m.Add("__meta_dockerswarm_node_platform_architecture", node.Description.Platform.Architecture)View on GitHub (pinned to 5079fb58f1)
Solutions
- Inspect the raw /nodes response body with the same credentials
- Ensure the response is a JSON array of node objects
- Test with an equivalent Docker API version to rule out schema drift
- Bypass intermediate proxies to rule out body substitution
Example fix
null
Defensive patterns
Strategy: type-guard
Validate before calling
// Go: reject non-array payloads before decoding into []node
if len(data) == 0 || data[0] != '[' {
return fmt.Errorf("/nodes returned non-array payload: %.200s", data)
} Type guard
// Go: narrow via a lightweight probe decode
func isNodeArray(data []byte) bool {
var probe []json.RawMessage
return json.Unmarshal(data, &probe) == nil
} Try / catch
null
Prevention
- Compare raw daemon output after every Docker Engine upgrade
- Disable body-rewriting middleboxes on the API path
- Assert application/json content type in a preflight check
When it happens
Trigger: json.Unmarshal(data, &nodes) errors: body is an HTML error page, empty, or a JSON object instead of an array; a field type changed in a newer Docker engine version.
Common situations: MitM proxy or SSO gateway returning HTML; Docker Engine upgrade changing node JSON types; daemon returning an error object {"message":...} with a 200 that then fails unmarshal into an array.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- cannot parse networks: %w
- cannot parse services: %w
- cannot parse tasks: %w
- unexpected empty json
- unexpected non json object {} type: %q
AI-assisted analysis of VictoriaMetrics/VictoriaMetrics@5079fb58f1 (2026-09-03).
Data as JSON: /api/errors/56427390d538354d.
Report an issue: GitHub.