dotnet/aspnetcore · error · InvalidOperationException

The {nameof(ParameterView)} instance can no longer be read b

Error message

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.

What it means

A ParameterView obtained during one render batch was read after the batch advanced (its validity stamp no longer matches the owning RenderBatchBuilder). ParameterView is a synchronous, snapshot-style struct; storing it across renders or async gaps and reading it later triggers this InvalidOperationException from ParameterViewLifetime.AssertNotExpired.

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 3600ca084e)

Solutions

  1. Extract every value you need from ParameterView synchronously at the top of SetParametersAsync into local/field copies.
  2. Never store the ParameterView itself; if you must defer, copy into a Dictionary<string,object?> first.
  3. Re-fetch parameters via a fresh read in the next lifecycle call instead of reusing the snapshot.

Example fix

// before
public override async Task SetParametersAsync(ParameterView parameters)
{
    _savedParams = parameters; // stored
    await DoWork();
    var x = _savedParams.GetValueOrDefault<int>("Count"); // expired!
}

// after
public override async Task SetParametersAsync(ParameterView parameters)
{
    _count = parameters.GetValueOrDefault<int>("Count");
    await DoWork();
}
Defensive patterns

Strategy: validation

Validate before calling

// Always copy out values synchronously
public override Task SetParametersAsync(ParameterView parameters)
{
    _count = parameters.GetValueOrDefault<int>(nameof(Count));
    _name = parameters.GetValueOrDefault<string>(nameof(Name));
    return base.SetParametersAsync(parameters);
}

Prevention

When it happens

Trigger: Storing a ParameterView in a field and reading it in OnAfterRenderAsync, a timer callback, or after an await. Capturing `parameters` from SetParametersAsync into a Task and indexing it later. Passing ParameterView to another component/method that defers the read.

Common situations: Manually caching parameters for async work. Forgetting to copy needed values out of ParameterView at the top of SetParametersAsync. Component frameworks that try to forward ParameterView to children outside the synchronous scope.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/67a815315e849c45. Report an issue: GitHub.