{"record":{"id":"1fb53e5783ed72fe","repo":"glanceapp/glance","slug":"w-v-1fb53e","errorCode":null,"errorMessage":"%w: %v","messagePattern":"%w: %v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/glance/widget-weather.go","lineNumber":237,"sourceCode":"\t} else {\n\t\ttemperatureUnit = \"celsius\"\n\t}\n\n\tquery.Add(\"latitude\", fmt.Sprintf(\"%f\", place.Latitude))\n\tquery.Add(\"longitude\", fmt.Sprintf(\"%f\", place.Longitude))\n\tquery.Add(\"timeformat\", \"unixtime\")\n\tquery.Add(\"timezone\", place.Timezone)\n\tquery.Add(\"forecast_days\", \"1\")\n\tquery.Add(\"current\", \"temperature_2m,apparent_temperature,weather_code\")\n\tquery.Add(\"hourly\", \"temperature_2m,precipitation_probability\")\n\tquery.Add(\"daily\", \"sunrise,sunset\")\n\tquery.Add(\"temperature_unit\", temperatureUnit)\n\n\trequestUrl := \"https://api.open-meteo.com/v1/forecast?\" + query.Encode()\n\trequest, _ := http.NewRequest(\"GET\", requestUrl, nil)\n\tresponseJson, err := decodeJsonFromRequest[openMeteoWeatherResponseJson](defaultHTTPClient, request)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w: %v\", errNoContent, err)\n\t}\n\n\tnow := time.Now().In(place.location)\n\tbars := make([]weatherColumn, 0, 24)\n\tcurrentBar := now.Hour() / 2\n\tsunriseBar := (time.Unix(int64(responseJson.Daily.Sunrise[0]), 0).In(place.location).Hour()) / 2\n\tsunsetBar := (time.Unix(int64(responseJson.Daily.Sunset[0]), 0).In(place.location).Hour() - 1) / 2\n\n\tif sunsetBar < 0 {\n\t\tsunsetBar = 0\n\t}\n\n\tif len(responseJson.Hourly.Temperature) == 24 {\n\t\ttemperatures := make([]int, 12)\n\t\tprecipitations := make([]bool, 12)\n\n\t\tt := responseJson.Hourly.Temperature\n\t\tp := responseJson.Hourly.PrecipitationProbability","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/glanceapp/glance/blob/91324e8de762702e97b0ac5c8e36271d644d8642/internal/glance/widget-weather.go#L219-L255","documentation":"The forecast fetch for a resolved place failed: decodeJsonFromRequest against api.open-meteo.com/v1/forecast returned an error (transport failure or non-200), wrapped as errNoContent so the widget shows its no-content state.","triggerScenarios":"GET https://api.open-meteo.com/v1/forecast?... returning 429 (Open-Meteo free-tier rate limit), 5xx, or the request failing at the network layer from the Glance host.","commonSituations":"Frequent page loads with short cache hitting Open-Meteo's per-day request quota; transient upstream outage; blocked egress from a containerized deployment.","solutions":["Inspect the nested %v — a 429 with 'API key' context means you exceeded the free tier and should cache longer or supply a paid key where supported","Raise the weather widget's cache duration (it already caches on the hour by default) and avoid hard-refresh storms","Verify connectivity from the host to api.open-meteo.com and retry after transient 5xx"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isNoContent(err error) bool { return errors.Is(err, errNoContent) }","tryCatchPattern":"w, err := fetchWeatherForOpenMeteoPlace(place, units)\nif errors.Is(err, errNoContent) {\n    // transient forecast fetch failure: keep last good data or retry after backoff\n    slog.Warn(\"weather fetch failed\", \"err\", err)\n    return lastGoodWeather\n}","preventionTips":["Cache weather results — the widget already caches on the hour; avoid shorter overrides on rate-limited deployments","Check errors.Is(err, errNoContent) and degrade to a cached/stale forecast instead of an error page","Watch the nested cause to distinguish 429 (back off) from network outage (retry later)"],"tags":["weather","network","open-meteo","rate-limit","no-content"],"backgroundTag":null,"analyzedSha":"91324e8de762702e97b0ac5c8e36271d644d8642","analyzedAt":"2026-08-15T14:12:54.279Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}