dotnet / dotnet/aspnetcore

Recreate a deleted temp directory instead of failing all later buffered requests

Open
#68,622 1 comment 0 reactions 0 assignees View on GitHub
area-networking
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Describe the bug

When ASP.NET Core spills a large multipart request or a large buffered response to disk, it writes `ASPNETCORE_*.tmp` under `ASPNETCORE_TEMP` or `Path.GetTempPath()`.

`AspNetCoreTempDirectory` caches that path for the process lifetime. If the directory is later missing (disk cleanup, an operator deleting the folder, Windows removing `Temp\{sessionId}`), the framework throws `DirectoryNotFoundException` instead of creating the directory it already resolved. Later requests that exceed the memory threshold keep failing until something outside the process recreates the folder.

For `IFormFile` binding this happens in `FormFeature` **before** user endpoint code runs, so the app cannot recover inside the action. Setting `ASPNETCORE_TEMP` to a “stable” folder does not help if that folder is deleted while the process is still running.

Related but not a duplicate of https://github.com/dotnet/aspnetcore/issues/42647 (locked). That issue was answered as: `%TEMP%` does not update after process start, so point `ASPNETCORE_TEMP` at a directory that already exists. This report is the other failure mode: **the path is already known, but the directory was deleted.**

### Expected Behavior

Before creating `ASPNETCORE_*.tmp`, call `Directory.CreateDirectory` on the already resolved path (no-op if it exists). If create or the subsequent open fails (permissions, read-only volume), throw as today.

Do not change how the path is chosen (`ASPNETCORE_TEMP` then `Path.GetTempPath()`). Do not try to refresh `%TEMP%` or allocate a new Windows session-id folder.

### Steps To Reproduce

1. Host an API that accepts `IFormFile` (multipart upload larger than `FormOptions.MemoryBufferThreshold` / the rewind threshold).
2. Optionally set `ASPNETCORE_TEMP` to a dedicated folder (the workaround from #42647), or use the default `Path.GetTempPath()`.
3. Ensure the folder exists, start the app, and confirm the upload succeeds.
4. Delete that folder. Do not restart the process.
5. Repeat the same upload.

### Exceptions (if any)

```text
2026-08-18 17:26:12.676 [DBG] Reading the request body failed with an IOException. |7|System.IO.DirectoryNotFoundException: C:\Users\Administrator\AppData\Local\Temp\2\
at Obfuscation.Web!+0x391f5
at Obfuscation.Web!+0x1090d1
at Obfuscation.Web!+0x10eeb0
--- End of stack trace from previous location ---
at Obfuscation.Web!+0x3767b0
at Obfuscation.Web!+0x378d07
at Obfuscation.Web!+0x378c2b
at Obfuscation.Web!+0x111b70
--- End of stack trace from previous location ---
at Obfuscation.Web!+0x3767b0
at Obfuscation.Web!+0x378d07
at Obfuscation.Web!+0x378c2b
at Obfuscation.Web!+0x4377e
--- End of stack trace from previous location ---
at Obfuscation.Web!+0x3767b0
at Obfuscation.Web!+0x378d07
at Obfuscation.Web!+0x378c2b
at Obfuscation.Web!+0x1d1e12
```

Often wrapped as:

```
Reading the request body failed with an IOException.
```

Typical stacks:

- Request: `FormFeature.InnerReadFormAsync` → `FileBufferingReadStream.CreateTempFile` → `AspNetCoreTempDirectory.TempDirectory` or `new FileStream(...)`
- Response: `FileBufferingWriteStream.EnsureFileStream` (same as #42647)

### .NET Version

10.0.400

### Anything else?

- ASP.NET Core version: current `main` (also observed on shipped versions that use `AspNetCoreTempDirectory`).
- OS: Windows (also applies anywhere the resolved temp directory can be deleted while the process is running).

Call path for `IFormFile`:

1. `FormFeature.InnerReadFormAsync` (`src/Http/Http/src/Features/FormFeature.cs`)
2. `section.EnableRewind(...)` → `FileBufferingReadStream` (`src/Http/Http/src/Internal/BufferingHelper.cs`)
3. Over threshold → `FileBufferingReadStream.CreateTempFile()` (`src/Http/WebUtilities/src/FileBufferingReadStream.cs`)
4. `AspNetCoreTempDirectory` currently throws if the directory is missing, and caches the path after the first successful lookup

I have a small patch ready (create the resolved directory in `AspNetCoreTempDirectory` and again immediately before creating the temp file in `FileBufferingReadStream` / `FileBufferingWriteStream`) and can open a PR against `main` if this approach is acceptable.

Contributor guide

Open the contributing guide

Research direction

Start with FormFeature.InnerReadFormAsync and follow BufferingHelper to FileBufferingReadStream.CreateTempFile; inspect AspNetCoreTempDirectory and the corresponding FileBufferingWriteStream path. Reproduce by deleting ASPNETCORE_TEMP after the first buffered request, then retry a request and response over the threshold. Done means the resolved directory is recreated and buffering succeeds, while permission or read-only failures still surface.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.