tailscale/tailscale · error
fetching %s: %s
Error message
fetching %s: %s
What it means
updateMap received a non-200 HTTP status when fetching the DERP map from d.derpMapURL (for example https://login.tailscale.com/derpmap/default or a self-hosted mirror); res.Status is appended. Note the cached-map fallback in updateMap only applies to transport errors - a non-200 status aborts immediately.
Source
Thrown at prober/derp.go:592
req, err := http.NewRequestWithContext(ctx, "GET", d.derpMapURL, nil)
if err != nil {
return err
}
res, err := httpOrFileClient.Do(req)
if err != nil {
d.Lock()
defer d.Unlock()
if d.lastDERPMap != nil && time.Since(d.lastDERPMapAt) < 10*time.Minute {
log.Printf("Error while fetching DERP map, using cached one: %s", err)
// Assume that control is restarting and use
// the same one for a bit.
return nil
}
return err
}
defer res.Body.Close()
if res.StatusCode != 200 {
return fmt.Errorf("fetching %s: %s", d.derpMapURL, res.Status)
}
dm = new(tailcfg.DERPMap)
if err := json.NewDecoder(res.Body).Decode(dm); err != nil {
return fmt.Errorf("decoding %s JSON: %v", d.derpMapURL, err)
}
}
d.Lock()
defer d.Unlock()
d.lastDERPMap = dm
d.lastDERPMapAt = time.Now()
d.nodes = make(map[string]*tailcfg.DERPNode)
for _, reg := range d.lastDERPMap.Regions {
if d.skipRegion(reg) {
continue
}
for _, n := range reg.Nodes {View on GitHub (pinned to 5201273aec)
Solutions
- curl -i the derpMapURL and read the exact status code and body
- Fix the -derp-map URL if it is 404/403 (path typo, moved file)
- For 429/5xx, retry after the rate limit or incident clears - the prober re-fetches each cycle
- Self-host the derpmap JSON at a stable URL if the upstream is flaky
Example fix
// before prober, _ := NewDerpProber(logf, "https://example.com/derpmap", ..., ...) // after prober, _ := NewDerpProber(logf, "https://example.com/derpmap/default.json", ..., ...) // correct path returning 200
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight the map URL before handing it to the prober
resp, err := http.Head(derpMapURL)
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("derp map URL unhealthy (%v, status %v); using last known map", err, resp.Status)
} Try / catch
if err := prober.UpdateMap(ctx); err != nil {
if strings.Contains(err.Error(), "fetching") {
// non-200 from control: keep the previous map, retry next cycle
// (transport errors already fall back to the cache inside updateMap)
}
} Prevention
- Monitor the derpMapURL with a plain HTTP up/down check alongside the prober
- Self-host a mirror of the map if you need immunity from control-plane blips
- Treat 429 seriously: back off rather than hammering the URL each probe cycle
When it happens
Trigger: The map URL returns 403/404/429/5xx: wrong or moved URL, auth-gated mirror, rate limiting, or a control/CDN incident.
Common situations: Typo in the -derp-map flag; corporate proxy intercepting the request; login.tailscale.com outage; a self-hosted derpmap endpoint behind auth or moved paths.
Related errors
- could not find derp node %s
- %s -> %s: %w
- sending packet %w
- got data packet from unexpected source, %v
- timeout: %w
AI-assisted analysis of tailscale/tailscale@5201273aec (2026-08-18).
Data as JSON: /api/errors/0b6327b037e2b9be.
Report an issue: GitHub.