dotnet/AspNetCore.Docs · error · IOException
No weather forecast!
Error message
No weather forecast!
What it means
The `ServerWeatherForecaster` sample deserializes the downstream API response as `WeatherForecast[]` and throws `IOException("No weather forecast!")` when the result is null. ReadFromJsonAsync returns null for an empty body or JSON `null`, so this converts a silent null into an explicit failure.
Source
Thrown at aspnetcore/blazor/call-web-api.md:1072
> [!NOTE]
> In non-`Production` environments, the preceding example uses <xref:Azure.Identity.DefaultAzureCredential> to simplify authentication while developing apps that deploy to Azure by combining credentials used in Azure hosting environments with credentials used in local development. When moving to production, an alternative is a better choice, such as the <xref:Azure.Identity.ManagedIdentityCredential> shown in the preceding example. For more information, see [Authenticate Azure-hosted .NET apps to Azure resources using a system-assigned managed identity](/dotnet/azure/sdk/authentication/system-assigned-managed-identity).
Inject <xref:Microsoft.Identity.Abstractions.IDownstreamApi> and call <xref:Microsoft.Identity.Abstractions.IDownstreamApi.CallApiForUserAsync%2A> when calling on behalf of a user:
```csharp
internal sealed class ServerWeatherForecaster(IDownstreamApi downstreamApi) : IWeatherForecaster
{
public async Task<IEnumerable<WeatherForecast>> GetWeatherForecastAsync()
{
var response = await downstreamApi.CallApiForUserAsync("DownstreamApi",
options =>
{
options.RelativePath = "/weather-forecast";
});
return await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
throw new IOException("No weather forecast!");
}
}
```
This approach is used by the `BlazorWebAppEntra` and `BlazorWebAppEntraBff` sample apps described in the *Sample apps* section of this article.
For more information, see the following resources:
* <xref:security/data-protection/implementation/key-storage-providers#azure-storage>
* <xref:security/data-protection/configuration/overview#protect-keys-with-azure-key-vault-protectkeyswithazurekeyvault>
* [Use the Azure SDK for .NET in ASP.NET Core apps](/dotnet/azure/sdk/aspnetcore-guidance?tabs=api)
* [Web API documentation | Microsoft identity platform](/entra/identity-platform/index-web-api)
* [A web API that calls web APIs: Call an API: Option 2: Call a downstream web API with the helper class](/entra/identity-platform/scenario-web-api-call-api-call-api?tabs=aspnetcore#option-2-call-a-downstream-web-api-with-the-helper-class)
* <xref:Microsoft.Identity.Abstractions.IDownstreamApi>
* *Secure an ASP.NET Core Blazor Web App with Microsoft Entra ID*
* [With YARP and Aspire (Interactive Auto)](xref:blazor/security/blazor-web-app-entra?pivots=with-yarp-and-aspire)
* [Without YARP and Aspire (Interactive Auto)](xref:blazor/security/blazor-web-app-entra?pivots=without-yarp-and-aspire)
* [Host ASP.NET Core in a web farm: Data Protection](xref:host-and-deploy/web-farm#data-protection)View on GitHub (pinned to c67a80103a)
Solutions
- Check the downstream endpoint actually returns a JSON array — call it directly and inspect the body/status.
- Verify the access token was obtained (`GetTokenAsync("access_token")`) and the downstream accepted it.
- Ensure `WeatherForecast` has a parameterless constructor and settable properties so deserialization succeeds.
- Handle the IOException at the call site and fall back to cached/empty data only if appropriate.
Example fix
// before
return await response.Content.ReadFromJsonAsync<WeatherForecast[]>()
?? throw new IOException("No weather forecast!");
// after (surface the real cause)
response.EnsureSuccessStatusCode();
var forecasts = await response.Content.ReadFromJsonAsync<WeatherForecast[]>()
?? throw new IOException($"No weather forecast! Status {(int)response.StatusCode}, length {response.Content.Headers.ContentLength}"); Defensive patterns
Strategy: validation
Validate before calling
public async Task<IEnumerable<WeatherForecast>> GetWeatherForecastAsync()
{
var response = await downstreamApi.CallApiForUserAsync("DownstreamApi",
o => o.RelativePath = "/weather-forecast");
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
if (string.IsNullOrWhiteSpace(body))
throw new IOException($"Empty body, status {(int)response.StatusCode}");
return JsonSerializer.Deserialize<WeatherForecast[]>(body)
?? throw new IOException("Deserialized to null");
} Type guard
static bool IsForecastPayload(WeatherForecast[]? arr) => arr is { Length: > 0 }; Try / catch
try
{
return await forecaster.GetWeatherForecastAsync();
}
catch (IOException ex) when (ex.Message.Contains("No weather forecast"))
{
logger.LogWarning(ex, "Downstream returned no forecast");
return Array.Empty<WeatherForecast>();
} Prevention
- Call `EnsureSuccessStatusCode` before deserializing.
- Verify WeatherForecast has a parameterless constructor and public setters.
- Log status + body length when null is returned to find the root cause.
When it happens
Trigger: The downstream `/weather-forecast` endpoint returns an empty response body, a JSON `null`, or a payload that deserializes to null. The null-coalescing throw then fires.
Common situations: Downstream API returned 204 No Content or an empty 200; route mismatch returning a different shape; auth/token failure resulting in a non-JSON error page that deserializes to null; deserialization type mismatch (private setter, missing parameterless constructor) collapsing the array to null.
Related errors
- Validation failed. Status Code: {response.StatusCode}
- QR code size must be less than {MaxQrSize}.
- worker exports not loaded
- Unknown command: ${e.data.command}
- HttpContext not available
AI-assisted analysis of dotnet/AspNetCore.Docs@c67a80103a (2026-08-13).
Data as JSON: /api/errors/9d2d081b62bd8af1.
Report an issue: GitHub.