Permission denied when writing to local directory, even after factory rebuild

Hello,

My application is failing on startup with a permission error when trying to create a local cache directory (./hf_cache).

Link to my Space: [Paste the URL to your Hugging Face Space here]

Error Log:

===== Application Startup at 2025-10-10 10:45:33 =====

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/local/lib/python3.12/site-packages/uvicorn/__main__.py", line 4, in <module>
    uvicorn.main()
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1462, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1383, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1246, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 814, in invoke
    return callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/uvicorn/main.py", line 423, in main
    run(
  File "/usr/local/lib/python3.12/site-packages/uvicorn/main.py", line 593, in run
    server.run()
  File "/usr/local/lib/python3.12/site-packages/uvicorn/server.py", line 67, in run
    return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 195, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "uvloop/loop.pyx", line 1518, in uvloop.loop.Loop.run_until_complete
  File "/usr/local/lib/python3.12/site-packages/uvicorn/server.py", line 71, in serve
    await self._serve(sockets)
  File "/usr/local/lib/python3.12/site-packages/uvicorn/server.py", line 78, in _serve
    config.load()
  File "/usr/local/lib/python3.12/site-packages/uvicorn/config.py", line 438, in load
    self.loaded_app = import_from_string(self.app)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/uvicorn/importer.py", line 19, in import_from_string
    module = importlib.import_module(module_str)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 999, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/app/main.py", line 7, in <module>
    from router import user_routes, auth_routes, croplist_routes, wallet_routes, contract_routes, milestone_routes, logistics_routes, chat_routes, service_routes, community_routes
  File "/app/router/service_routes.py", line 6, in <module>
    from services.cotton_model import cotton_disease_model
  File "/app/services/cotton_model.py", line 40, in <module>
    cotton_disease_model = CottonDiseaseModel()
                           ^^^^^^^^^^^^^^^^^^^^
  File "/app/services/cotton_model.py", line 19, in __init__
    self._load_model()
  File "/app/services/cotton_model.py", line 23, in _load_model
    model_path = hf_hub_download(repo_id=self.repo_id, filename=self.filename, cache_dir="./hf_cache")
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/huggingface_hub/utils/_validators.py", line 114, in _inner_fn
    return fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/huggingface_hub/file_download.py", line 1010, in hf_hub_download
    return _hf_hub_download_to_cache_dir(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/huggingface_hub/file_download.py", line 1127, in _hf_hub_download_to_cache_dir
    os.makedirs(os.path.dirname(blob_path), exist_ok=True)
  File "<frozen os>", line 215, in makedirs
  File "<frozen os>", line 215, in makedirs
  File "<frozen os>", line 225, in makedirs
PermissionError: [Errno 13] Permission denied: './hf_cache'

Ihave already tried all the standard troubleshooting steps without success:

  • My code is correctly configured to write to a local path (./hf_cache), not the root directory.

  • I have tried restarting the Space.

  • I have performed a full factory rebuild.

Since the error persists after a factory rebuild, it seems like there is an issue with the file system permissions on the underlying hardware for my Space. Could you please investigate?

Thank you!

1 Like

Directories within Spaces where users have write permissions are quite limited. Directories starting with a period (.) or those directly under the root directory are almost impossible. They may also be inaccessible. It’s best to assume you can’t use directories outside the user home directory. (There are some exceptions as noted below…)


Root cause: your code tries to create ./hf_cache under the app repo at runtime. In Spaces the runtime user often cannot write to the repo/workdir. Use a writable absolute path: /data if you enabled Persistent storage, else /tmp. Redirect Hugging Face caches or pass cache_dir to hf_hub_download. This is a permissions/layout issue, not hardware. (Hugging Face)

What’s happening (background + context)

  • Spaces storage layout. Your code runs inside a container. Persistent disk, when enabled, is mounted at /data and is writable. Other paths may be read-only or owned by a different user. (Hugging Face)

  • Runtime user. Docker Spaces run as UID 1000. If your repo files are owned by root or your code writes relative to . (repo checkout), writes can fail with [Errno 13]. (Hugging Face)

  • HF caching defaults. huggingface_hub and related libs write under ~/.cache/huggingface unless you override with HF_HOME, HF_HUB_CACHE, TRANSFORMERS_CACHE, etc., or pass cache_dir to hf_hub_download. If that default resolves to an unwritable place, you get a PermissionError. (Hugging Face)

  • Seen in the wild. Multiple forum threads report identical PermissionErrors in Spaces fixed by pointing caches to a writable dir like /data or /tmp. Dates: 2023-07-13, 2024-05-19, 2025-03-22. (Hugging Face Forums)

Fast, safe fix (no Docker changes)

  1. Enable Persistent storage in your Space settings. This gives you /data that survives restarts. If you skip this, use /tmp which is ephemeral. (Hugging Face)

  2. Set env vars in the Space → Settings → Variables so all HF libs write under /data:


HF_HOME=/data/.huggingface

HF_HUB_CACHE=/data/.cache/huggingface/hub

TRANSFORMERS_CACHE=/data/.cache/huggingface/transformers

HF_DATASETS_CACHE=/data/.cache/huggingface/datasets

These are the officially supported knobs. They redirect all downloads, snapshots, and token files. (Hugging Face)

  1. Change your code to stop writing ./hf_cache. Use an absolute, writable cache root, prefer /data, fall back to /tmp:

# refs:

# - HF env vars: https://huggingface.co/docs/huggingface_hub/en/package_reference/environment_variables

# - Cache dir param: https://huggingface.co/docs/huggingface_hub/en/guides/download

import os, pathlib

from huggingface_hub import hf_hub_download

# honor env if provided; otherwise choose a writable default

cache_root = (

os.getenv("HF_HUB_CACHE")

or ("/data/hf_cache" if os.path.isdir("/data") else "/tmp/hf_cache")

)

pathlib.Path(cache_root).mkdir(parents=True, exist_ok=True)

model_path = hf_hub_download(

repo_id="your/repo",

filename="yourfile.bin",

cache_dir=cache_root, # absolute and writable

)

Passing cache_dir is supported and recommended when you control the path. (Hugging Face)

  1. Defer downloads until app startup instead of at import time. Your stack trace shows a download during module import. Failures there kill the process before the server starts. Do the download in an app “startup” hook or first-request path. (Hugging Face)

  2. Quick sanity check to verify writability:


import os, pathlib

p = pathlib.Path(os.getenv("HF_HUB_CACHE", "/data/.cache/huggingface/hub"))

print(p, "exists:", p.exists(), "writable:", os.access(p, os.W_OK))

If writable is False, switch to /tmp/... or fix Docker user/ownership. (Hugging Face)

If you are using a Docker Space

Match the runtime user and set caches to /data. This avoids permission flips between build-time root and run-time UID 1000.


# refs:

# - Docker Spaces permissions: https://huggingface.co/docs/hub/en/spaces-sdks-docker

# - First Docker Space guide: https://huggingface.co/docs/hub/en/spaces-sdks-docker-first-demo

RUN useradd -m -u 1000 user

USER user

ENV HOME=/home/user PATH=/home/user/.local/bin:$PATH

WORKDIR $HOME/app

COPY --chown=user . $HOME/app

# put HF caches on the persistent volume if enabled

ENV HF_HOME=/data/.huggingface \

HF_HUB_CACHE=/data/.cache/huggingface/hub \

TRANSFORMERS_CACHE=/data/.cache/huggingface/transformers

These lines mirror the official guidance and fix typical [Errno 13] cache errors. (Hugging Face)

Why this solves it

  • /data is the documented persistent, writable mount for Spaces. Redirecting caches there removes permission failures and keeps downloads across restarts. (Hugging Face)

  • hf_hub_download(cache_dir=...) and HF_* env vars are the supported ways to move caches. They exist for containerized runtimes like Spaces. (Hugging Face)

  • UID 1000 at runtime plus root-owned files during build causes write failures unless you set the user and ownership properly. The docs warn about this. (Hugging Face)

Common pitfalls to avoid

  • Writing under . or ~ without confirming ownership. Use absolute paths. (Hugging Face)

  • Assuming caches persist in /tmp. They vanish on restart. Prefer /data if you need persistence. (Hugging Face)

  • Changing file permissions to 777 in containers. This can mask the real problem and still fail under UID 1000 on redeploy. Move caches instead. Background discussion exists, but official guidance is to set caches and user. (Stack Overflow)

Short reference set (checked Oct 11, 2025)

  • Spaces persistent storage: /data mount, persists across restarts. (Hugging Face)

  • HF cache control: HF_HOME, HF_HUB_CACHE, cache_dir in hf_hub_download. (Hugging Face)

  • Docker Spaces permissions: runtime UID 1000, set user, WORKDIR, ownership. (Hugging Face)

  • Similar issues and fixes: forum threads confirming permission-denied symptoms and /data or env-var fixes. (Hugging Face Forums)

If you enable storage and apply the env vars, your current PermissionError: './hf_cache' will stop. If you want, share the exact repo snippet where hf_hub_download is called and I’ll show the minimal diff to adopt /data or /tmp.