{"record":{"id":"2733549bd0877abc","repo":"tailscale/tailscale","slug":"invalid-derp-map-json-w","errorCode":null,"errorMessage":"invalid derp map json: %w","messagePattern":"invalid derp map json: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/local/local.go","lineNumber":1163,"sourceCode":"\t\treturn nil, errors.New(\"http Transport did not provide a writable body\")\n\t}\n\treturn netutil.NewAltReadWriteCloserConn(rwc, switchedConn), nil\n}\n\n// CurrentDERPMap returns the current DERPMap that is being used by the local tailscaled.\n// It is intended to be used with netcheck to see availability of DERPs.\n//\n// API maturity: this is considered a stable API, though the returned\n// [tailcfg.DERPMap] type is subject to minor changes over time; its\n// general shape is stable.\nfunc (lc *Client) CurrentDERPMap(ctx context.Context) (*tailcfg.DERPMap, error) {\n\tvar derpMap tailcfg.DERPMap\n\tres, err := lc.send(ctx, \"GET\", \"/localapi/v0/derpmap\", 200, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err = json.Unmarshal(res, &derpMap); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid derp map json: %w\", err)\n\t}\n\treturn &derpMap, nil\n}\n\n// CertDomains returns the list of domains for which the local tailscaled can\n// fetch TLS certificates, equivalent to the DNS.CertDomains field of the\n// current netmap. The returned list is sorted in ascending order, and is\n// empty if no netmap has been received yet.\n//\n// API maturity: this is considered a stable API.\nfunc (lc *Client) CertDomains(ctx context.Context) ([]string, error) {\n\tbody, err := lc.get200(ctx, \"/localapi/v0/cert-domains\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn decodeJSON[[]string](body)\n}\n","sourceCodeStart":1145,"sourceCodeEnd":1181,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/client/local/local.go#L1145-L1181","documentation":"Thrown by Client.CurrentDERPMap (client/local/local.go:1163) when GET /localapi/v0/derpmap returned 200 but json.Unmarshal into tailcfg.DERPMap failed. The DERPMap originates from the control plane and is cached by the daemon, so a shape mismatch means version skew between library and daemon or a nonstandard control plane.","triggerScenarios":"Daemon and client library from different releases with changed tailcfg.DERPMap fields (DERPMap is explicitly subject to minor shape changes); a headscale or custom control serving a nonstandard derpmap that the daemon passes through verbatim; proxy on the socket.","commonSituations":"Netcheck-style tooling linked against a newer tailscale.com module than the node's daemon; mixed fleets where a central monitor queries many daemon versions.","solutions":["Align the client library version with the daemon ('tailscale version') and restart tailscaled","If using headscale or a custom control, upgrade it to emit a DERPMap matching your library's version","Inspect the raw body: curl --unix-socket /var/run/tailscale/tailscaled.sock http://local-tailscaled.sock.localapi.net/localapi/v0/derpmap","Pin the tailscale.com module to the same version as the daemon fleet"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"st, err := lc.Status(ctx)\nif err != nil { return err }\nif st.Version == \"\" { return errors.New(\"cannot determine tailscaled version\") }","typeGuard":"func isJSONDecodeError(err error) bool {\n    var se *json.SyntaxError\n    var te *json.UnmarshalTypeError\n    return errors.As(err, &se) || errors.As(err, &te)\n}","tryCatchPattern":"dm, err := lc.CurrentDERPMap(ctx)\nif err != nil {\n    if isJSONDecodeError(err) {\n        // DERPMap shape drifts by design: pin library to daemon version\n    }\n    return nil, err\n}","preventionTips":["Remember DERPMap is only shape-stable, not exact: re-pin the module on daemon upgrades","With headscale, upgrade it in lockstep with the client library"],"tags":["go","tailscale","localapi","json","version-skew","derp"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}