{"record":{"id":"7c7e59a0df87ea47","repo":"tailscale/tailscale","slug":"creating-map-request-w","errorCode":null,"errorMessage":"creating map request: %w","messagePattern":"creating map request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"control/tsp/map.go","lineNumber":309,"sourceCode":"\t\tStream:    false,\n\t\tReadOnly:  false,\n\t}\n\n\tbody, err := json.Marshal(mapReq)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"encoding map request: %w\", err)\n\t}\n\n\tnc, err := c.noiseClient(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"establishing noise connection: %w\", err)\n\t}\n\n\turl := c.serverURL + \"/machine/map\"\n\turl = strings.Replace(url, \"http:\", \"https:\", 1)\n\treq, err := http.NewRequestWithContext(ctx, \"POST\", url, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"creating map request: %w\", err)\n\t}\n\tts2021.AddLBHeader(req, opts.NodeKey.Public())\n\n\tres, err := nc.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"map request: %w\", err)\n\t}\n\tdefer res.Body.Close()\n\n\tif res.StatusCode != 200 {\n\t\tmsg, _ := io.ReadAll(res.Body)\n\t\treturn fmt.Errorf(\"map request: http %d: %.200s\",\n\t\t\tres.StatusCode, strings.TrimSpace(string(msg)))\n\t}\n\tio.Copy(io.Discard, res.Body)\n\treturn nil\n}\n","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/control/tsp/map.go#L291-L327","documentation":"Returned by Client.SendMapUpdate when http.NewRequestWithContext rejects the URL constructed as c.serverURL + \"/machine/map\". NewRequestWithContext fails only when url.Parse fails: control characters in the URL, whitespace/newlines, a missing or malformed scheme/host, or an unusable method. This is purely local URL validation; nothing has been sent.","triggerScenarios":"ServerURL loaded from config without a scheme (e.g. \"controlplane.tailscale.com\"), containing a trailing newline or space from a file/env var, or containing a control character. The strings.Replace(http:→https:) step does not repair a malformed base URL.","commonSituations":"Reading ServerURL from an env var or .env file with a trailing CR/LF; YAML config parsed with a stray quote; URL pasted with an embedded space; empty ServerURL plus a modified DefaultServerURL.","solutions":["Normalize and validate ServerURL once at startup with url.Parse: require a non-empty Host and scheme http/https","Trim whitespace and strip line endings from config-supplied URLs","Log the exact composed URL (c is internal, so mirror it from your ClientOpts.ServerURL + \"/machine/map\") when debugging"],"exampleFix":"// before\nserverURL := os.Getenv(\"TS_SERVER_URL\") // \"https://control.example.com\\n\"\nc, _ := tsp.NewClient(tsp.ClientOpts{MachineKey: mk, ServerURL: serverURL})\n\n// after\nserverURL := strings.TrimSpace(os.Getenv(\"TS_SERVER_URL\"))\nu, err := url.Parse(serverURL)\nif err != nil || u.Host == \"\" || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n    return fmt.Errorf(\"bad TS_SERVER_URL %q\", serverURL)\n}\nc, _ := tsp.NewClient(tsp.ClientOpts{MachineKey: mk, ServerURL: serverURL})","handlingStrategy":"validation","validationCode":"u, err := url.Parse(serverURL)\nif err != nil || u.Host == \"\" || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n    return fmt.Errorf(\"invalid control server URL %q\", serverURL)\n}\nc, err := tsp.NewClient(tsp.ClientOpts{MachineKey: mk, ServerURL: serverURL})","typeGuard":"func validControlURL(raw string) bool {\n    u, err := url.Parse(strings.TrimSpace(raw))\n    return err == nil && u.Host != \"\" && (u.Scheme == \"http\" || u.Scheme == \"https\")\n}","tryCatchPattern":"if err := c.SendMapUpdate(ctx, opts); err != nil {\n    if strings.HasPrefix(err.Error(), \"creating map request\") {\n        // deterministic config bug: the URL is malformed; fail fast, do not retry\n        return fmt.Errorf(\"check ServerURL config: %w\", err)\n    }\n}","preventionTips":["Normalize every externally sourced URL with strings.TrimSpace and url.Parse before use","Fail config validation at process start rather than at first RPC","Add config schema tests that reject scheme-less or whitespace-bearing URLs"],"tags":["go","url","validation","config","sendmapupdate"],"backgroundTag":"invalid-url","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}