googleapis/mcp-toolbox · error

failed to unmarshal json: %v

Error message

failed to unmarshal json: %v

What it means

Thrown by the Dgraph source's healthCheck when the /health response body cannot be unmarshaled into the expected JSON array of health entries (instance, address, status). Dgraph returned a 200/body that is not the expected health JSON — often an HTML error page, a proxy message, or an empty body. Wrapped with %v.

Source

Thrown at internal/sources/dgraph/dgraph.go:370

	resp, err := hc.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("error performing request: %w", err)
	}

	defer resp.Body.Close()
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var result []struct {
		Instance string `json:"instance"`
		Address  string `json:"address"`
		Status   string `json:"status"`
	}

	// Unmarshal response into the struct
	if err := json.Unmarshal(data, &result); err != nil {
		return fmt.Errorf("failed to unmarshal json: %v", err)
	}

	if len(result) == 0 {
		return fmt.Errorf("health info should not empty for: %v", url)
	}

	var unhealthyErr error
	for _, info := range result {
		if info.Status != "healthy" {
			unhealthyErr = fmt.Errorf("dgraph instance [%v] is not in healthy state, address is %v",
				info.Instance, info.Address)
		} else {
			return nil
		}
	}

	return unhealthyErr
}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. curl the exact /health URL and confirm the body is a JSON array like [{"instance":"...","address":"...","status":"healthy"}].
  2. Ensure baseUrl points at the Dgraph Alpha/Zero HTTP port, not a proxy, UI, or gRPC port.
  3. Check whether an intermediary (nginx/ingress) is intercepting the request and returning non-JSON responses.
  4. Verify the Dgraph version still serves the same /health schema.

Example fix

// before: pointing at the UI/proxy
baseUrl: http://dgraph-ui:8080
// after: point at the Alpha HTTP endpoint
baseUrl: http://dgraph-alpha:8080
Defensive patterns

Strategy: validation

Validate before calling

resp, err := http.Get(baseURL + "/health")
if err != nil { return err }
body, _ := io.ReadAll(resp.Body)
var probe []map[string]interface{}
if err := json.Unmarshal(body, &probe); err != nil {
    return fmt.Errorf("/health returned non-JSON body (first 100 bytes: %q) — check for proxy/UI at this address", string(body[:min(100, len(body))]))
}

Try / catch

if err := healthCheck(ctx); err != nil {
    if strings.Contains(err.Error(), "failed to unmarshal json") {
        // non-JSON body: capture the raw response for diagnosis
        log.Printf("dgraph /health returned unexpected body; raw: %s", rawBody)
        return err
    }
    return err
}

Prevention

When it happens

Trigger: healthCheck reads the /health response with io.ReadAll and json.Unmarshal(data, &result) fails because the body is not a JSON array of objects with instance/address/status fields (e.g. HTML from a reverse proxy, or a JSON object instead of an array).

Common situations: A load balancer or ingress returns an HTML 502/404 page; pointing baseUrl at the Dgraph HTTP UI or a different service that returns different JSON; a Dgraph version changing the health payload shape.

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


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/2baeda78e08ee3fc. Report an issue: GitHub.