{"record":{"id":"5d9cf3cf3f912a6e","repo":"dotnet/aspnetcore","slug":"the-parameterview-instance-can-no-longer-be-read-b","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/Rendering/ParameterViewLifetime.cs","lineNumber":25,"sourceCode":"{\n    private readonly RenderBatchBuilder _owner;\n    private readonly int _stamp;\n\n    public static readonly ParameterViewLifetime Unbound;\n\n    public ParameterViewLifetime(RenderBatchBuilder owner)\n    {\n        _owner = owner;\n        _stamp = owner.ParameterViewValidityStamp;\n    }\n\n    public void AssertNotExpired()\n    {\n        // If _owner is null, this instance is default(ParameterViewLifetime), which is\n        // the same as ParameterViewLifetime.Unbound. That means it never expires.\n        if (_owner != null && _owner.ParameterViewValidityStamp != _stamp)\n        {\n            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.\");\n        }\n    }\n}\n","sourceCodeStart":7,"sourceCodeEnd":29,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Components/Components/src/Rendering/ParameterViewLifetime.cs#L7-L29","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Extract all needed values from ParameterView synchronously at the top of SetParametersAsync, before any await, and store those values in fields.","Never store the ParameterView itself; copy individual parameter values out immediately.","If you need to read parameters asynchronously, capture the values into local variables before awaiting.","For deferred logic, pass extracted values (strings, ints, etc.) to the deferred code, never the ParameterView."],"exampleFix":"// before\npublic override async Task SetParametersAsync(ParameterView parameters)\n{\n    await SomeAsyncWork();\n    var name = parameters.TryGetValue<string>(\"Name\", out var n) ? n : null; // throws: expired\n}\n\n// after\npublic override async Task SetParametersAsync(ParameterView parameters)\n{\n    var name = parameters.TryGetValue<string>(\"Name\", out var n) ? n : null;\n    await SomeAsyncWork(); // read parameters first\n}","handlingStrategy":"validation","validationCode":"// Extract all parameter values synchronously at the top of SetParametersAsync\npublic override Task SetParametersAsync(ParameterView parameters)\n{\n    // Read everything BEFORE any await\n    _name = parameters.TryGetValue<string>(\"Name\", out var n) ? n : null;\n    _count = parameters.TryGetValue<int>(\"Count\", out var c) ? c : 0;\n    return base.SetParametersAsync(parameters);\n}","typeGuard":"null // ParameterView is a struct; no type guard applies. The guard is procedural: read synchronously.","tryCatchPattern":null,"preventionTips":["Never store ParameterView in a field; extract primitive values immediately.","Read all parameters at the top of SetParametersAsync, before any await.","Use a Roslyn analyzer or code review checklist to flag deferred ParameterView access.","In async methods, capture needed values into locals before the first await."],"tags":["blazor","parameterview","async","lifecycle","setparameters"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}