dotnet/aspnetcore · error · InvalidOperationException

The ParameterView instance can no longer be read because it

Error message

The ParameterView instance can no longer be read because it has expired. ParameterView can only be read synchronously and must not be stored for later use.

What it means

ParameterView is a read-only view over a mutable internal buffer used during parameter assignment. The buffer is reused across renders, so reading a ParameterView after the synchronous SetParametersAsync phase risks reading stale or overwritten data. ParameterViewLifetime stamps the view at creation and checks the stamp on every read; if the underlying buffer's stamp has changed (because a new render batch started), it throws InvalidOperationException.

Source

Thrown at src/Components/Components/src/Rendering/ParameterViewLifetime.cs:25

{
    private readonly RenderBatchBuilder _owner;
    private readonly int _stamp;

    public static readonly ParameterViewLifetime Unbound;

    public ParameterViewLifetime(RenderBatchBuilder owner)
    {
        _owner = owner;
        _stamp = owner.ParameterViewValidityStamp;
    }

    public void AssertNotExpired()
    {
        // If _owner is null, this instance is default(ParameterViewLifetime), which is
        // the same as ParameterViewLifetime.Unbound. That means it never expires.
        if (_owner != null && _owner.ParameterViewValidityStamp != _stamp)
        {
            throw new InvalidOperationException($"The {nameof(ParameterView)} instance can no longer be read because it has expired. {nameof(ParameterView)} can only be read synchronously and must not be stored for later use.");
        }
    }
}

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Extract all needed values from ParameterView synchronously at the top of SetParametersAsync, before any await, and store those values in fields.
  2. Never store the ParameterView itself; copy individual parameter values out immediately.
  3. If you need to read parameters asynchronously, capture the values into local variables before awaiting.
  4. For deferred logic, pass extracted values (strings, ints, etc.) to the deferred code, never the ParameterView.

Example fix

// before
public override async Task SetParametersAsync(ParameterView parameters)
{
    await SomeAsyncWork();
    var name = parameters.TryGetValue<string>("Name", out var n) ? n : null; // throws: expired
}

// after
public override async Task SetParametersAsync(ParameterView parameters)
{
    var name = parameters.TryGetValue<string>("Name", out var n) ? n : null;
    await SomeAsyncWork(); // read parameters first
}
Defensive patterns

Strategy: validation

Validate before calling

// Extract all parameter values synchronously at the top of SetParametersAsync
public override Task SetParametersAsync(ParameterView parameters)
{
    // Read everything BEFORE any await
    _name = parameters.TryGetValue<string>("Name", out var n) ? n : null;
    _count = parameters.TryGetValue<int>("Count", out var c) ? c : 0;
    return base.SetParametersAsync(parameters);
}

Type guard

null // ParameterView is a struct; no type guard applies. The guard is procedural: read synchronously.

Prevention

When it happens

Trigger: Storing a ParameterView in a field or capturing it in a closure, then reading it later. Common triggers: reading parameters after an await in SetParametersAsync (the buffer may be recycled during the async gap); saving ParameterView for use in OnAfterRenderAsync or an event handler; passing ParameterView to a background task.

Common situations: Async SetParametersAsync that reads ParameterView after await Task.Yield() or an I/O call; storing ParameterView in a field to defer reading; capturing ParameterView in a timer callback or event subscription; misunderstanding that ParameterView is a transient view, not a snapshot.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/5d9cf3cf3f912a6e. Report an issue: GitHub.