UK tech experts · info@vividrepairs.co.uk
Vivid Repairs
Windows terminal showing Python uvicorn process running with current working directory path printed in command prompt output
Fix It Yourself · Troubleshooting

uvicorn working directory recovery

Published 19 September 202613 min read
As an Amazon Associate, we may earn from qualifying purchases. Our ranking is independent.

We see this one constantly in remote support sessions. Someone's python -m uvicorn main:app --reload process is running, something's broken with paths or imports, and they have no idea what directory the process actually started from. The good news: uvicorn working directory recovery is possible in most cases, and the fix is usually quicker than people expect. The bad news: Windows stores no history of the original directory, so if you haven't logged it, you're reconstructing from clues.

TL;DR

For uvicorn working directory recovery on Windows, add print(os.getcwd()) to main.py and let --reload restart the process. If no os.chdir() calls exist in your code, the printed value equals the original launch directory. For deeper inspection, expose a diagnostics endpoint or check your IDE run config, shortcut Start In field, or Task Scheduler action.

⏱️ 13 min read

Key Takeaways

  • Windows keeps no history of the original working directory. Recovery means reading the current value and reasoning about whether it has changed.
  • If your code never calls os.chdir(), the current directory equals the one inherited at launch. That's your answer.
  • The fastest route to uvicorn working directory recovery is adding one print statement and saving the file so --reload does the work.
  • Task Scheduler and Windows services often default to C:\Windows\System32 when no working directory is set explicitly. That's a very common cause of module-not-found errors.
  • Preventing this in future takes about two minutes: log os.getcwd() at startup and set an explicit cd /d in your launch batch file.

At a Glance

  • Difficulty: Intermediate
  • Time Required: 5 to 30 mins depending on method

What Causes Uvicorn Working Directory Recovery Problems?

The root of this problem is a Windows limitation that surprises a lot of developers: the operating system records the current working directory of a running process, but it keeps absolutely no record of what that directory was at the moment the process was created. There's no audit trail, no event log entry, nothing. The Windows GetCurrentDirectory API gives you the live value, and that's all you get.

So where does the confusion actually come from in practice? A few places.

The most common one: someone opens a terminal, navigates to a project folder, starts uvicorn, then opens a second terminal tab and forgets which directory the first session was in. Or they close the original terminal entirely and the process keeps running in the background. Now they need to know the CWD and there's no obvious way to ask.

The second most common cause is IDE run configurations. VS Code, PyCharm, and similar tools all have a working directory setting in their run/debug configs. If that field is blank or set to the wrong folder, uvicorn inherits a CWD that doesn't match the project root. The process starts, uvicorn tries to find main:app relative to that directory, and either fails immediately or silently uses the wrong paths for file operations. This is especially dodgy when multiple projects are open in the same IDE window.

Third: os.chdir() in your own code or a dependency. This is the nasty one. Python inherits the parent shell's directory at launch and keeps it until something calls os.chdir(). If your app or a library changes the working directory at runtime, then os.getcwd() no longer reflects the original launch directory. You'd need to search the codebase for chdir calls to understand what happened.

Fourth: Task Scheduler and Windows services. When you launch a Python process through Task Scheduler without setting the 'Start in' field, Windows defaults the working directory to C:\Windows\System32. Every relative path in your app then resolves against that system folder. Imports fail, file reads fail, and the error messages can be genuinely confusing because they reference paths that look almost right.

Fifth: shortcuts. A Windows shortcut has a 'Start in' field under Properties. If that field is empty or wrong, the process inherits the shortcut file's own directory, which is rarely your project root.

Understanding which of these applies to your situation is the first step in uvicorn working directory recovery. The solutions below go from fastest to most thorough.

Uvicorn Working Directory Recovery: Quick Fix

This works if the process is still running and you can edit the source code. It takes about five minutes.

1

Print the CWD via Hot Reload Easy

  1. Open main.py
    Add these two lines near the very top, before your app definition:
    import os
    print('Current working directory:', os.getcwd())
  2. Save the file
    The --reload flag watches for file changes and restarts the process automatically. You don't need to kill and relaunch anything manually.
  3. Read the terminal output
    Look for a line like:
    Current working directory: C:\Users\You\Projects\MyApp
    That is the live CWD. If your code has never called os.chdir(), this is also the original launch directory.
  4. Check for chdir calls
    In VS Code, press Ctrl+Shift+F and search for chdir across the whole project. In a terminal, run:
    findstr /s /i chdir *.py
    If nothing comes back, the printed directory is your original working directory. Job done.
You should see the full path printed in the uvicorn terminal output immediately after the reload. That's your working directory confirmed.
The Python os.getcwd() documentation confirms that this function returns the current working directory of the process as a string. It reflects whatever directory was inherited at launch, unless something has called os.chdir() since then. That's the key assumption the quick fix relies on.

One thing people miss: the --reload mechanism in uvicorn uses watchfiles (or watchgod on older versions) to detect changes. Sometimes on Windows, especially over network drives or with certain antivirus tools running, the reload can be slow or miss a save. If the print statement doesn't appear after saving, try touching the file again or restarting uvicorn manually this once.

Also, if you're running uvicorn inside a Docker container on Windows, the CWD inside the container is what matters, not the Windows host path. The two can look completely different. In that case, the print statement approach still works, but the path you see will be the container's filesystem path, not a Windows path.

More Uvicorn Working Directory Recovery Solutions

If you can't edit the code right now, or if the quick fix didn't give you a clear answer, these intermediate approaches use Windows configuration interfaces to reconstruct how the process was launched.

2

Check the Parent Shell and IDE Config Easy

  1. Check the terminal window
    If the terminal that launched uvicorn is still open, run cd with no arguments in Command Prompt, or pwd in PowerShell. This shows the shell's current directory. If you haven't navigated away since starting uvicorn, this is the launch directory.
  2. Check VS Code run configuration
    Open the Command Palette with Ctrl+Shift+P, type 'Open launch.json', and look for a cwd field in the relevant configuration. If it's absent, VS Code defaults to the workspace root folder, which is usually the project root. Check that against the os.getcwd() output.
  3. Check PyCharm run configuration
    Go to Run, Edit Configurations. Select the uvicorn run config and look at the 'Working directory' field. This is the directory PyCharm passes to the process at launch.
If the IDE working directory field matches the os.getcwd() output from the quick fix, you've confirmed the original launch directory from two independent sources.
3

Inspect Shortcuts and Task Scheduler Medium

  1. Check a Windows shortcut
    Right-click the shortcut that launches your uvicorn process. Choose Properties. Look at the 'Start in' field. Whatever path is there becomes the working directory for the process. If it's blank, the process inherits the directory of the shortcut file itself, which is usually not your project root.
  2. Check Task Scheduler
    Open Task Scheduler from the Start menu. Find the task that runs python or uvicorn. Click it, then go to the Actions tab. Select the action and click Edit. The 'Start in (optional)' field is the working directory. If it's empty, Windows defaults to C:\Windows\System32, which explains a lot of import failures.
  3. Cross-reference with uvicorn logs
    If uvicorn logged any absolute file paths at startup (for example, when it found or failed to find main.py), those paths tell you what directory it was searching. A log line referencing C:\Users\You\Projects\MyApp\main.py confirms the working directory was C:\Users\You\Projects\MyApp.
Once the Start in field or Task Scheduler action confirms the directory, you have the original working directory even without touching the running process.
If Task Scheduler shows an empty Start in field and your process is misbehaving with path errors, that is almost certainly your problem. The process is running from C:\Windows\System32 and every relative path in your app is resolving there. Fix it by setting the Start in field to your project root and restarting the task.

Just as a misconfigured network adapter can cause connectivity headaches similar to a Vodafone router not working situation where the fix is buried in a settings panel you wouldn't normally check, the Task Scheduler Start in field is exactly that kind of hidden gotcha. It looks optional. It isn't, really.

Advanced Uvicorn Working Directory Recovery Fixes

These approaches go deeper. Use them when the process is long-running, you can't edit source code, or you need to be certain about the working directory for a production service.

4

Expose a Diagnostics Endpoint Medium

  1. Add a protected route to your FastAPI or Starlette app
    This is the cleanest approach for a running service you can't restart:
    import os
    from pathlib import Path
    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get('/_diag/cwd')
    def get_cwd():
        return {
            'os_getcwd': os.getcwd(),
            'pathlib_cwd': str(Path.cwd())
        }
    Save and let --reload restart. Then hit http://localhost:8000/_diag/cwd in a browser.
  2. Protect it in production
    Add an API key check or IP restriction to this route before deploying. Exposing server paths publicly is a security issue. In dev, it's fine as-is.
  3. Read the response
    Both os_getcwd and pathlib_cwd should return the same path. If they differ, something unusual is happening with the Python environment. The os.getcwd() value is the authoritative one.
The endpoint returns a JSON object with the live working directory. No terminal access needed, no process restart required beyond the initial reload.
5

Reason from File Paths and Error Behaviour Advanced

  1. Read the uvicorn startup output carefully
    When uvicorn starts, it logs the app it's loading. If it prints something like Loading module 'main' from C:\Users\You\Projects\MyApp, that directory is the CWD. If it fails with ModuleNotFoundError: No module named 'main', the CWD is wrong and doesn't contain main.py.
  2. Trace FileNotFoundError messages
    Any FileNotFoundError using a relative path tells you what the CWD is expected to be. If your code does open('data/config.json') and gets a FileNotFoundError, Python was looking for {CWD}\data\config.json. If you know where config.json actually lives, you can work backwards to what the CWD must have been for the error to occur.
  3. Understand the Windows API layer
    At the OS level, the GetCurrentDirectory Windows API is what Python's os.getcwd() calls internally. There is genuinely no Windows API for querying the directory at process creation time. This isn't a Python limitation. It's a Windows design. Recovery always means reading the current value and reasoning about whether it has changed.
Combining error messages, log output, and knowledge of the launch method gives you a confident reconstruction of the original working directory even without modifying code.
The uvicorn settings documentation confirms that uvicorn adds the current working directory to sys.path at startup. This is why the CWD matters so much for module resolution. It's not just about file paths in your code. It's about whether Python can find your app module at all.

Here's the thing: if you're hitting this problem repeatedly, it's a sign that the project's launch setup isn't documented well enough. That's not a criticism. It happens on every team eventually. But the fix isn't just recovering the directory this time. It's making sure you never have to recover it again. See the prevention section below.

One edge case that catches people out: if your app uses a library that calls os.chdir() internally (some older test frameworks and certain file-processing libraries do this), the CWD can change without any obvious sign in your own code. The findstr /s /i chdir *.py search only covers your project files. To catch library calls, you'd need to monkeypatch os.chdir at startup to log a traceback whenever it's called. That's a proper debugging technique for the really stubborn cases.

And just as a keyboard shortcut failing at the OS level, like a left Ctrl key not working in Windows, can have causes buried several layers deep in hardware and driver config, a wrong working directory can have causes buried in service wrappers, virtual environment activations, or parent process inheritance chains that aren't obvious from the surface.

Preventing Uvicorn Working Directory Recovery Issues

Prevention here is genuinely easy. The problem is that nobody does it until they've been burned once.

The single most useful habit: log os.getcwd() at startup. Not to the console, but to a file. Add this near the top of main.py:

import os, logging
logging.basicConfig(filename='startup.log', level=logging.INFO)
logging.info('Working directory at startup: %s', os.getcwd())

That file persists across restarts. Next time you need to know the original directory, it's already recorded. Two minutes of work, saves an hour of head-scratching later.

Second: stop relying on the CWD for file paths in your application code. Use Path(__file__).parent instead. This gives you the directory of the source file itself, which is stable regardless of what directory the process was launched from. So instead of open('data/config.json'), write open(Path(__file__).parent / 'data' / 'config.json'). Your code then works correctly no matter where uvicorn was started from.

Third: write a proper launch batch file. Something like:

@echo off
cd /d C:\Users\You\Projects\MyApp
python -m uvicorn main:app --reload

The cd /d sets both the drive and directory explicitly. No ambiguity. Run this batch file from anywhere and uvicorn always starts from the right place. Store it in the project root and commit it to version control.

Fourth: if you're using Task Scheduler, always fill in the 'Start in' field. Always. It's optional in the UI but it's not optional in practice. Set it to your project root and you'll never hit the C:\Windows\System32 problem again.

Fifth: document the expected launch directory in your README. One line: 'Run uvicorn from the project root: C:\Projects\MyApp'. That's it. Saves the next developer (or future you) from having to figure it out from scratch.

One more thing that's easy to overlook: if your project uses environment variables for configuration, add MYAPP_ROOT=C:\Projects\MyApp to your .env file and reference that variable in code instead of relying on the CWD. It's a bit like how display signal problems, similar to an HDMI to USB-C not working scenario, are much easier to prevent by using the right cable from the start than to diagnose after the fact. Setting the root variable explicitly means you never have to reconstruct it.

Uvicorn Working Directory Recovery: Summary

Uvicorn working directory recovery on Windows comes down to one core fact: Windows stores no history of the original directory, so you're always reading the current value and reasoning about whether it's changed. If your code doesn't call os.chdir(), the current value is the original. The fastest recovery method is adding print(os.getcwd()) to main.py and letting --reload do the restart. For running services you can't touch, expose a diagnostics endpoint or inspect the Task Scheduler action and IDE run configuration. And going forward, log the working directory at startup, use Path(__file__).parent for file paths, and always set an explicit directory in your batch files and Task Scheduler tasks. Uvicorn working directory recovery shouldn't need to happen twice on the same project.

Quick Reference

  • Fastest fix: Add print(os.getcwd()) to main.py, save, read the --reload output
  • No code access: Check IDE run config, shortcut Start In field, or Task Scheduler action
  • Production service: Add a protected /_diag/cwd endpoint returning os.getcwd()
  • If paths are wrong: Check for os.chdir() calls in your code and dependencies
  • Prevent it: Log CWD at startup, use Path(__file__).parent, set explicit Start In fields everywhere

Frequently Asked Questions

No. Windows only stores the current directory via GetCurrentDirectory and keeps no history of the original. Unless you logged it at startup, you have to infer it from the current value and knowledge of whether os.chdir() was ever called.

Only if no code has called os.chdir() since the process started. Python inherits the parent shell directory at launch and keeps it until something changes it, so without os.chdir() the current value equals the original.

Add import os and print('CWD:', os.getcwd()) to main.py, then save the file. The --reload mechanism restarts the process and prints the value in your terminal. Read it there.

Task Scheduler, Windows services, and misconfigured shortcuts often default to a system directory when no explicit working directory is set. Relative paths and imports then fail because the CWD is nowhere near your project.

Yes. Uvicorn resolves the app module relative to the CWD and adds it to sys.path. A wrong CWD causes module-not-found errors even if your code is perfectly correct.