Table of Contents

CHT001: ValueTask awaited multiple times

Cause

A ValueTask or ValueTask<T> variable is awaited more than once.

Rule Description

A ValueTask is a struct that can only be consumed once. Unlike Task, which can be safely awaited multiple times, awaiting a ValueTask multiple times leads to undefined behavior. When backed by an IValueTaskSource, the second await may throw InvalidOperationException or return incorrect results.

How to Fix

Option 1: Convert to Task at declaration

If you need to await the result multiple times, convert to Task immediately:

// Before
ValueTask vt = GetValueTask();
await vt;
await vt; // Error

// After
Task t = GetValueTask().AsTask();
await t;
await t; // OK

Option 2: Use Preserve()

Use the Preserve() extension method for safe conversion:

using CryptoHives.Foundation.Threading;

Task t = GetValueTask().Preserve();
await t;
await t; // OK

Option 3: Store the result

If you need the result multiple times, await once and store the result:

// Before
ValueTask<int> vt = GetValueTask();
int a = await vt;
int b = await vt; // Error

// After
int result = await GetValueTask().ConfigureAwait(false);
int a = result;
int b = result; // OK

When to Suppress

Never suppress this diagnostic. Awaiting a ValueTask multiple times is always a bug.

Example

Violating Code

public async Task ProcessAsync()
{
    ValueTask vt = DoWorkAsync();
    
    if (someCondition)
    {
        await vt;
    }
    
    await vt; // CHT001: vt already awaited
}

Fixed Code

public async Task ProcessAsync()
{
    Task t = DoWorkAsync().AsTask();
    
    if (someCondition)
    {
        await t;
    }
    
    await t; // OK - Task can be awaited multiple times
}

See Also