Question

How to Check Whether a File Is in Use in C#

csharp.netfilefile-iofile-locking

Question

I am writing a C# program that repeatedly accesses a single image file. Most of the time it works, but on a fast system the program sometimes tries to read the file before it has finished being written to disk, and an error is thrown:

The process cannot access the file because it is being used by another process.

I would like to avoid this problem. So far, most solutions I have found rely on catching exceptions when the file is locked. I would prefer a cleaner approach if possible. Is there a better way in C# to check whether a file is in use before trying to access it?

Short Answer

By the end of this page, you will understand why checking whether a file is "in use" is harder than it first appears, why a pre-check alone is unreliable, and how C# programs typically handle this safely using file access rules, retries, and exception handling where necessary.

Concept

In C#, a file can be temporarily unavailable because another process has opened it with a lock or with restrictive sharing rules. This often happens when one program is still writing the file while another tries to read it.

The important idea is this:

  • There is no perfectly reliable "check first, then use" approach for files.
  • A file's state can change immediately after you check it.
  • This is a classic race condition.

For example, imagine you ask:

  1. "Is the file free right now?"
  2. The answer is yes.
  3. Another process locks the file one millisecond later.
  4. Your program tries to open it and still fails.

That is why exception handling is not just a fallback here—it is often the correct and necessary approach.

In real programming, the best solution usually combines:

  • trying to open the file directly
  • using the correct FileShare mode
  • retrying for a short period if the file is still being written
  • handling exceptions that can still occur

This matters because file access is shared with the operating system and other processes. Your code does not fully control when another program opens, writes, closes, or replaces a file.

Mental Model

Think of a file like a meeting room.

  • If someone is inside and has locked the door, you cannot enter.
  • You can peek at the sign and see whether the room looks free.
  • But between checking the sign and opening the door, someone else might walk in and lock it.

So the only reliable test is actually trying the door.

That is how files work too:

  • a pre-check can be out of date instantly
  • the real test is attempting to open the file
  • if it fails, your program must decide whether to wait, retry, or give up
Take Quiz

Syntax and Examples

In C#, the common pattern is to try opening the file with a FileStream.

using System.IO;

bool IsFileAvailable(string path)
{
    try
    {
        using (FileStream stream = new FileStream(
            path,
            FileMode.Open,
            FileAccess.Read,
            FileShare.None))
        {
            return true;
        }
    }
    catch (IOException)
    {
        return false;
    }
}

What this does

  • FileMode.Open tries to open an existing file.
  • FileAccess.Read requests read access.
  • FileShare.None says no other process may already be using it in a compatible way.
  • If the open succeeds, the file is available at that moment.
  • If an IOException occurs, the file is likely locked, still being written, or otherwise unavailable.

However, this function is only useful as a momentary test. It does not guarantee the file will still be free when you use it later.

Better pattern: retry when opening the file

using System;
using System.IO;
using System.Threading;

FileStream OpenFileWithRetry(string path, int maxRetries = 5, int delayMs = 200)
{
    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
        }
        catch (IOException)
        {
            if (i == maxRetries - 1)
                throw;

            Thread.Sleep(delayMs);
        }
    }

    throw new IOException("Unable to open file after retries.");
}

Why this is better

Instead of checking first and then opening later, this pattern:

  • attempts the real operation immediately
  • waits briefly if the file is not ready
  • retries a few times
  • still fails cleanly if the file never becomes available

That is usually the most practical approach.

Step by Step Execution

Consider this example:

using System;
using System.IO;
using System.Threading;

string path = "image.png";

for (int attempt = 1; attempt <= 3; attempt++)
{
    try
    {
        using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            Console.WriteLine($"Opened on attempt {attempt}");
            break;
        }
    }
    catch (IOException)
    {
        Console.WriteLine($"Attempt {attempt} failed");
        Thread.Sleep(100);
    }
}

Step by step

  1. path is set to image.png.
  2. The loop starts with attempt = 1.
  3. The program tries to open the file.
  4. If another process is still writing the file, new FileStream(...) throws IOException.
  5. The catch block runs.
  6. The program prints Attempt 1 failed.
  7. It waits 100 milliseconds.
  8. The loop tries again.
  9. Once the other process finishes and closes the file, opening succeeds.
  10. The using block ensures the stream is closed properly.
  11. break exits the loop.

Key lesson

The program does not rely on a separate check. It performs the actual file operation and reacts safely if the file is temporarily unavailable.

Real World Use Cases

This concept appears often in real applications:

  • Image processing tools: waiting for a camera, screenshot tool, or editor to finish saving an image.
  • Log readers: reading log files that another process is actively writing.
  • Import jobs: watching a folder for CSV, JSON, or XML files that may arrive in stages.
  • Desktop automation: opening exported reports only after Excel or another tool has finished writing them.
  • File upload pipelines: processing files only after they are fully saved by another system.

In all of these cases, a file may exist but still not be ready to read safely.

Take Quiz

Real Codebase Usage

In production code, developers usually do not build a separate "is file in use" check and then act on it. Instead, they use patterns like these:

Guarded open with retry

  • Try to open the file immediately.
  • If it fails with an IOException, wait briefly and retry.
  • Stop after a maximum number of attempts.

This is simple and reliable.

Early return on missing file

Before worrying about locks, it is common to check whether the file exists:

if (!File.Exists(path))
    return;

This handles one problem early, while still understanding that existence does not guarantee availability.

Validation before processing

After opening the file, code may validate:

  • file size is greater than 0
  • image can actually be decoded
  • expected extension or format matches

Error handling and logging

Real systems often log retry attempts:

catch (IOException ex)
{
    logger.LogWarning(ex, "File not ready yet: {Path}", path);
}

Reading with compatible sharing

Sometimes the writer allows readers while writing. In those cases, developers may use a less restrictive share mode such as FileShare.ReadWrite. That only works if partial reads are acceptable and the producing process supports it.

File system watchers plus retry

When using FileSystemWatcher, the created event may fire before the file is fully written. The common solution is still: respond to the event, then retry opening the file until it is ready.

Common Mistakes

1. Checking first, then opening later

Broken idea:

if (IsFileAvailable(path))
{
    // Assume it is safe
    var text = File.ReadAllText(path);
}

Why it is a problem:

  • The file can become locked after the check.
  • This creates a race condition.

How to avoid it:

  • Perform the actual open/read inside the try block.
  • Retry if needed.

2. Treating exceptions as "bad design"

Beginners sometimes think exceptions should never be used for file access. But file I/O is an external system operation.

  • disks fail
  • files disappear
  • permissions change
  • other processes lock files

For these cases, exceptions are normal and expected.

3. Forgetting to close streams

Broken code:

FileStream stream = new FileStream(path, FileMode.Open);
// no Dispose or Close

Why it is a problem:

  • Your own program may keep the file locked.

Fix:

using FileStream stream = new FileStream(path, FileMode.Open);

4. Using the wrong FileShare value

Example:

new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None)

This is stricter than:

new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)

If you only need to read and shared reading is acceptable, an overly strict mode may cause unnecessary failures.

5. Retrying forever

An endless retry loop can freeze part of your program.

Better:

  • set a maximum retry count
  • set a delay between retries
  • log failure clearly if retries are exhausted

6. Assuming file existence means file readiness

if (File.Exists(path))
{
    // Not enough to prove the file is ready
}

A file can exist while still being written or locked.

Comparisons

ApproachHow it worksReliable?Best use case
File.Exists()Checks whether the file path currently existsNoQuick existence check only
Custom "is in use" checkTry to open the file separately, return true/falseNot fullyTemporary status checks
Open directly with try/catchPerform the real operation and handle failureYes, practicalMost real applications
Open with retryTry, wait, and try againYes, very practicalFiles that become available shortly
FileSystemWatcher onlyReact to file system eventsNoDetect changes, but not readiness

FileShare.None vs FileShare.Read

OptionMeaningWhen to use
FileShare.NoneNo other sharing allowedStrict lock check
FileShare.ReadOther readers may also open the fileSafe shared reading
FileShare.ReadWriteReaders and writers may share accessOnly if partial/in-progress content is acceptable

Main takeaway

If your goal is to actually use the file, opening it directly with retries is usually better than asking whether it is currently in use.

Take Quiz

Cheat Sheet

// Momentary availability check
bool IsFileAvailable(string path)
{
    try
    {
        using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None);
        return true;
    }
    catch (IOException)
    {
        return false;
    }
}
// Practical pattern: open with retry
FileStream OpenFileWithRetry(string path, int maxRetries = 5, int delayMs = 200)
{
    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
        }
        catch (IOException)
        {
            if (i == maxRetries - 1) throw;
            Thread.Sleep(delayMs);
        }
    }

    throw new IOException();
}

Rules to remember

  • File.Exists() does not mean the file is ready.
  • A separate check can still race with another process.
  • The most reliable test is trying the real operation.
  • Use using so streams are always closed.
  • Add retries for temporarily locked files.
  • Use a max retry count to avoid infinite waiting.
  • Choose FileShare carefully based on how much sharing is safe.

Common exceptions

  • IOException: file locked, busy, or other I/O problem
  • UnauthorizedAccessException: permission problem or invalid access

FAQ

Why can't I just check whether the file is in use first?

Because the file can change state immediately after the check. Another process may lock it before your next line of code runs.

Is using try/catch for file access really normal in C#?

Yes. File access depends on the operating system, disk, permissions, and other processes. Exceptions are the normal way to handle these failures.

What exception is usually thrown when a file is locked?

Usually IOException, though the exact reason may vary. Permission problems may throw UnauthorizedAccessException instead.

Should I use FileShare.None or FileShare.Read?

Use FileShare.None when you want strict exclusive access. Use FileShare.Read when shared reading is acceptable.

Can FileSystemWatcher tell me when the file is ready?

Not reliably. It tells you that something changed, not that writing is complete.

What is the best approach when another process is still saving the file?

Try opening the file, catch IOException, wait briefly, and retry a limited number of times.

Can I read a file while another process is writing it?

Sometimes, if the writer allows sharing and your code uses a compatible FileShare mode. But the content may be incomplete.

Take Quiz

Mini Project

Description

Build a small C# utility that waits for an image file to become readable, then reports its size. This demonstrates the practical pattern used in real applications: do not trust a separate availability check; instead, retry the actual file open until the file is ready or a timeout is reached.

Goal

Create a program that safely opens a file that may still be locked by another process and prints basic information once it becomes available.

Requirements

  • Ask the user for a file path.
  • Check that the file exists before retrying.
  • Retry opening the file a limited number of times.
  • Wait briefly between attempts.
  • Print the file size when the file opens successfully.
Take Quiz

Keep learning

Related questions

View more