Attachements not available on https://huggingface.co/proxy/agents-course-unit4-scoring.hf.space/docs#

Hi! :waving_hand:
I’m currently working on the GAIA evaluation agent, and I’ve run into an issue with the task attachments (images, audio files, Python code, Excel sheets, etc.).

According to the documentation, these files should be obtainable by calling the attachments endpoint with the task_id. However, the endpoint consistently returns no file, and it looks like attachments have not been available for quite a while.

Could someone from the Hugging Face team (ping @HuggingFace or the GAIA maintainers) confirm:

Are the task attachments going to be made available again?

If yes, is there an expected timeline for when access will be restored?

If not, should agents be designed to work without attachments for now?

This is blocking full GAIA evaluation support, so any clarification would be very appreciated. Thanks in advance! :folded_hands:

For now, I’ve organized the current situation (what’s happening in the code and the proposed fixes).


Short answer:

  • The Space broke because GAIA changed how it ships data, but the Space still assumes an old layout.
  • GAIA now stores file_path as a repo-relative path and no longer provides a loader script that materializes local files for you.(Hugging Face)
  • The Space checks os.path.exists(file_path) directly, so it never finds any file, never fills task_file_paths, and /files/{task_id} always returns 404.(Hugging Face)
  • The minimal fix: in load_questions(), replace the os.path.exists logic with a small hf_hub_download(...) call that turns the GAIA file_path into a real local path under /app/.cache.

Below is the explanation step by step, plus a concrete small patch.


1. What the Space is supposed to do

The scoring Space has three public endpoints (as described in the course materials and forum reply).(Hugging Face Forums)

  • GET /questions

    • Loads GAIA (gaia-benchmark/GAIA, config 2023_level1, split validation).
    • Filters down to “simple enough” tasks by annotator metadata.
    • Exposes a list of questions: task_id, question, Level, and (optionally) file_name.
  • GET /files/{task_id}

    • Uses an internal map task_id → local_file_path (task_file_paths).
    • Serves the file (image/audio/xlsx/py/etc.) with proper MIME type.
  • POST /submit

    • Checks your answers against Final answer.
    • Records your score in agents-course/unit4-students-scores.

All of this is wired in main.py of agents-course/Unit4_scoring.(Hugging Face)

The /files/{task_id} endpoint depends completely on task_file_paths being filled correctly at startup.


2. What changed on the GAIA side

Two important changes happened on GAIA’s side:

  1. Datasets 4.0 removed dataset-loader scripts.
    GAIA used to have a GAIA.py loader and had to be converted to a “script-free” dataset. The maintainers explicitly mention the need to drop the script and move to Parquet.(Hugging Face)

  2. GAIA now ships Parquet + repo-relative file_path.
    The updated GAIA dataset card (October 2025) says: (Hugging Face)

    • Splits are now Parquet: metadata.level1.parquet etc.

    • Columns remain task_id, Question, Level, Final answer, file_name, file_path, Annotator Metadata.

    • Crucial sentence:

      “file_path keeps pointing to attachments relative to the repository root (for example, 2023/test/<attachment-id>.pdf).”

    • The recommended loading pattern is:

      from datasets import load_dataset
      from huggingface_hub import snapshot_download
      import os
      
      data_dir = snapshot_download(repo_id="gaia-benchmark/GAIA", repo_type="dataset")
      dataset = load_dataset(data_dir, "2023_level1", split="test")
      for example in dataset:
          file_path = os.path.join(data_dir, example["file_path"])
      

    So: GAIA never promises that file_path is an absolute local path. It is a relative path inside the dataset repo.


3. How the Space currently loads GAIA and files

Look at the Space’s load_questions() in main.py: (Hugging Face)

dataset = load_dataset("gaia-benchmark/GAIA",
                       "2023_level1",
                       split="validation",
                       trust_remote_code=True)
...
local_file_path = item.get('file_path')
file_name = item.get('file_name')
...
# 3. Store the file path mapping if file details exist and are valid
if local_file_path and file_name:
    # Log if the path from the dataset isn't absolute (might indicate issues)
    if not os.path.isabs(local_file_path):
        logger.warning(
            f"Task {task_id}: Path '{local_file_path}' from dataset is not absolute. "
            "This might cause issues finding the file on the server."
        )

    if os.path.exists(local_file_path) and os.path.isfile(local_file_path):
        task_file_paths[str(task_id)] = local_file_path
        logger.debug(f"Stored file path mapping for task_id {task_id}: {local_file_path}")
    else:
        logger.warning(
            f"File path '{local_file_path}' for task_id {task_id} does NOT exist or is not a file on server. "
            "Mapping skipped."
        )

Key points:

  • It reads file_path from GAIA into local_file_path.
  • It warns if this path is not absolute (so it expects absolute paths).
  • Then it directly calls os.path.exists(local_file_path) and only stores it if that is true.

Later, /files/{task_id} does:

if task_id not in task_file_paths:
    raise HTTPException(status_code=404,
                        detail=f"No file path associated with task_id {task_id}.")
...
abs_file_path = os.path.abspath(local_file_path)
if not abs_file_path.startswith(ALLOWED_CACHE_BASE):
    raise HTTPException(status_code=403, detail="File access denied.")
if not os.path.exists(abs_file_path) or not os.path.isfile(abs_file_path):
    raise HTTPException(status_code=404,
                        detail=f"File associated with task_id {task_id} not found on server disk.")
return FileResponse(path=abs_file_path, ...)

(Hugging Face)

So /files/{task_id} will only work if:

  • task_file_paths[task_id] exists, and
  • that path points to a real file already on disk under /app/.cache.

4. Why this breaks now

Combine the previous sections:

  1. GAIA now exposes file_path as repo-relative, e.g. "2023/test/abcd1234.png".(Hugging Face)

  2. The scoring Space never downloads those files nor joins the path with the dataset root. It simply expects local_file_path to be an absolute path that already exists inside the container.(Hugging Face)

  3. At startup, for each question with an attachment:

    • local_file_path is something like "2023/test/abcd1234.png".
    • os.path.isabs("2023/test/abcd1234.png") is false, so it logs a warning.
    • os.path.exists("2023/test/abcd1234.png") is also false, because nothing at that relative path exists in the container filesystem.
    • So it skips adding an entry to task_file_paths.

    Result: task_file_paths ends up empty or nearly empty.

  4. At request time:

    • GET /files/{task_id} looks into task_file_paths.
    • It finds no entry and returns 404 "No file path associated with task_id ...".

This matches the forum symptom: even using the correct bare task_id in the URL, users get 404 for tasks with valid file_name.(Hugging Face Forums)

So the Space is broken because its file-path handling is out of date with GAIA’s new Parquet + relative file_path design.


5. Minimal code fix inside the Space

Goal: keep the architecture, change as little as possible.

You already have hf_hub_download imported:

from huggingface_hub import HfApi, hf_hub_download

(Hugging Face)

So the smallest safe fix is to replace the “does this path exist locally?” logic by a call that resolves GAIA’s relative file_path into a real local file.

Patch: only change the mapping block in load_questions()

Current block (lines 224–238): (Hugging Face)

        # 3. Store the file path mapping if file details exist and are valid
        if local_file_path and file_name:
            # Log if the path from the dataset isn't absolute (might indicate issues)

            if not os.path.isabs(local_file_path):
                logger.warning(
                    f"Task {task_id}: Path '{local_file_path}' from dataset is not absolute. "
                    "This might cause issues finding the file on the server."
                )

            if os.path.exists(local_file_path) and os.path.isfile(local_file_path):
                task_file_paths[str(task_id)] = local_file_path
                logger.debug(f"Stored file path mapping for task_id {task_id}: {local_file_path}")
            else:
                logger.warning(
                    f"File path '{local_file_path}' for task_id {task_id} does NOT exist or is not a file on server. "
                    "Mapping skipped."
                )

Replace that block with:

        # 3. Store the file path mapping if file details exist and are valid
        if local_file_path and file_name:
            try:
                # GAIA's file_path is relative to the dataset repo root.
                # Download the file into the allowed cache and get its local path.
                resolved_path = hf_hub_download(
                    repo_id="gaia-benchmark/GAIA",
                    filename=local_file_path,  # e.g. "2023/test/<attachment-id>.pdf"
                    repo_type="dataset",
                    cache_dir=ALLOWED_CACHE_BASE,
                )

                task_file_paths[str(task_id)] = resolved_path
                logger.debug(
                    f"Stored file path mapping for task_id {task_id}: {resolved_path}"
                )
            except Exception as e:
                logger.warning(
                    f"Could not download file '{local_file_path}' for task_id {task_id}: {e}. "
                    "Mapping skipped."
                )

Optional one-liner near the top of load_questions() (after task_file_paths.clear()):

    os.makedirs(ALLOWED_CACHE_BASE, exist_ok=True)

Why this is enough

  • Resolves GAIA semantics correctly

    • GAIA promises file_path is a path inside the dataset repo.(Hugging Face)
    • hf_hub_download(repo_id="gaia-benchmark/GAIA", filename=local_file_path, repo_type="dataset", ...) downloads exactly that file from the GAIA repo and returns its local path.(Hugging Face)
  • Keeps security checks intact

    • You set cache_dir=ALLOWED_CACHE_BASE so the returned resolved_path will be under /app/.cache/....
    • The /files/{task_id} endpoint already checks abs_file_path.startswith(ALLOWED_CACHE_BASE). That continues to work and blocks path traversal.(Hugging Face)
  • No changes to external API

    • /questions response stays the same.
    • /files/{task_id} stays the same URL shape and behavior, but now it can actually find files.
    • /submit is unaffected.
  • Minimal surface change

    • You do not touch:

      • The load_dataset("gaia-benchmark/GAIA", ...) call.
      • The FastAPI route definitions.
      • The scoring logic or leaderboard update code.

6. Sanity checks after patch

After deploying the patched Space, do three quick manual tests:

  1. Check questions still load

    curl https://huggingface.co/proxy/agents-course-unit4-scoring.hf.space/questions | head
    

    You should see JSON with task_id, question, Level, and sometimes file_name.

  2. Pick a known multimodal task

    From /questions, identify a question where file_name is not null (for example an image or mp3).

  3. Call /files/{task_id}

    curl -I "https://huggingface.co/proxy/agents-course-unit4-scoring.hf.space/files/<that-task-id>"
    

    Expected:

    • HTTP status 200 OK.
    • A reasonable Content-Type (e.g. image/png, audio/mpeg, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, etc.).

If you still get 404, log output from load_questions() will tell you whether hf_hub_download failed (dataset gating, network, token, etc.).


Final summary

  • The Space broke because it assumes GAIA’s file_path is an absolute local path, but GAIA now defines file_path as a relative path inside the dataset repo, with Parquet-backed splits and no loader script.(Hugging Face)
  • At startup, the Space never downloads those files or joins them with a dataset root, so os.path.exists(file_path) fails for every attachment, task_file_paths stays empty, and /files/{task_id} returns 404.(Hugging Face)
  • The minimal fix is to replace the os.path.exists block in load_questions() with a call to hf_hub_download(repo_id="gaia-benchmark/GAIA", filename=local_file_path, repo_type="dataset", cache_dir=ALLOWED_CACHE_BASE), then store that returned path in task_file_paths.
  • This respects GAIA’s new format, keeps security checks and public API unchanged, and restores working attachments for /files/{task_id}.

Thanks for the detailed investigation. I can confirm I’m seeing the same behavior.

The /questions endpoint correctly returns a file_name for attachment-based tasks. For example:

task_id: 7bd855d8-463d-4ed5-93ca-5fe35145f733
file_name: 7bd855d8-463d-4ed5-93ca-5fe35145f733.xlsx

However, requesting:

GET /files/7bd855d8-463d-4ed5-93ca-5fe35145f733

returns:

{"detail":"No file path associated with task_id 7bd855d8-463d-4ed5-93ca-5fe35145f733."}

I also verified this directly in the browser and from my code using requests.get(), with the same result (404).

This prevents my agent from supporting Excel (and presumably image, audio, and Python) attachment tasks, even though the questions advertise the corresponding file_name.

Has anyone from the Hugging Face team confirmed whether the official Unit4_scoring Space will be updated to resolve the attachment mapping issue?

Ah… :open_mouth: I only just realized that there was no PR open for this, so I opened one. If you need to unblock yourself immediately, the quickest route is probably to duplicate the scoring Space and apply the same fix. Otherwise, the next option is to wait for HF to review and merge the PR:


I still do not see a public confirmation or ETA from an HF maintainer. At the time of writing, the public Unit4_scoring Space still uses the old attachment-path logic, so the official /files/{task_id} endpoint remains affected.

However, there is now a minimal Space PR: Fix GAIA attachment downloads. The same failure is also tracked in agents-course issue #624 and issue #647.

Item Current status
HF maintainer confirmation / ETA I have not seen one publicly
Fix PR Open; currently shown as ready to merge
Official scoring Space Fix not yet applied
Private duplicate with the patch Tested successfully
Referenced attachments 5/5 returned HTTP 200

What appears to be failing

The current GAIA dataset documentation describes attachment file_path values as paths relative to the dataset repository root, for example:

2023/validation/<task-id>.xlsx

The current public Unit4_scoring/main.py, however, checks those values directly with os.path.exists() as though the files had already been materialized on the Space filesystem.

The resulting flow is approximately:

GAIA returns a repository-relative file_path
    ↓
the scoring Space checks it as an existing local path
    ↓
os.path.exists() returns false
    ↓
no task_id → local file mapping is stored
    ↓
/files/{task_id} returns:
"No file path associated with task_id ..."

This appears to be the failure point in the current public code.

The PR keeps the existing behavior for absolute local paths, but resolves repository-relative paths with hf_hub_download(). That downloads the attachment into the normal Hugging Face cache and gives the existing file-serving route a real local path.

The change is intentionally narrow: it does not restructure the API, question selection, submission logic, or dataset loader.

Validation

I tested the change end to end on a private duplicate of the current scoring Space, using the current runtime and a read token with GAIA access.

The duplicate:

  • loaded all 20 questions successfully;
  • downloaded and mapped all 5 referenced attachments;
  • returned HTTP 200 from /files/{task_id} for:
    • one PNG;
    • two MP3 files;
    • one Python file;
    • one XLSX file.

Before the patch, the startup log reported:

Stored file path mappings for 0 tasks.

After the patch:

Stored file path mappings for 5 tasks.

I also checked the returned filenames, byte counts, and basic file signatures, so this was not only a successful startup or metadata-only test.

The two practical options

If you can wait

Follow the Space PR and the existing #624 / #647 reports.

Once the PR is merged and the official Space is rebuilt, the public /files/{task_id} endpoint can be checked again.

If you need to continue now

Duplicate the scoring Space privately and apply the same change from the PR.

Important: this private scoring-Space duplicate is separate from your actual Agent / Final Assignment Space. Your assignment Space can remain public for the normal course workflow; only the temporary scoring duplicate should be private.

The scoring duplicate uses its owner’s token to retrieve files from the gated GAIA dataset. If its /files/{task_id} endpoint were made public, it could unintentionally expose gated validation attachments outside the normal GAIA access flow.

Immediate workaround: create a private scoring-Space duplicate
  1. Open the official agents-course/Unit4_scoring Space.

  2. Duplicate it into your own account.

  3. Keep the duplicate private.

    A protected Space is not equivalent for this use case: the source may be restricted, but the running application can still be publicly reachable. The file-serving application itself needs to remain private.

  4. Make sure your HF account has accepted the access conditions for the GAIA dataset.

    GAIA access is granted per user, so the account owning the token must have access.

  5. Create a read token and add it to the duplicate Space under:

    Settings → Variables and secrets → Secrets
    

    Use the name:

    HF_TOKEN
    

    Secrets from the original Space are not copied into a duplicate, so this must be added manually.

  6. Apply the change shown in PR #2.

    The essential behavior is:

    if file_path is repository-relative:
        download it from gaia-benchmark/GAIA
        use the returned local cache path
    else:
        keep the existing local-path behavior
    
  7. Rebuild the Space.

  8. Check the startup log.

    The useful sanity check is:

    Found 20 questions matching the criteria
    Stored file path mappings for 5 tasks
    
  9. Test one or more attachment routes.

    For example, the XLSX task should return HTTP 200 from:

    /files/7bd855d8-463d-4ed5-93ca-5fe35145f733
    

Calling the private duplicate

Because the duplicate is private, the agent or script calling it must authenticate with a token that can read that Space.

Store that token as a Secret in the calling environment and send it as a Bearer token. Do not place it in repository files, browser-side JavaScript, logs, or the Space README.

The same read token may be usable for both the private Space and GAIA when it belongs to an account with access to both, although keeping secrets narrowly scoped is preferable where practical.

Alternative workaround: resolve the attachment inside the agent

If authenticating every request to a private scoring-Space duplicate is inconvenient, the same attachment-resolution step can instead be placed inside the agent itself.

A reasonable split is:

/questions  → official scoring Space
attachment  → authenticated hf_hub_download() from GAIA
/submit     → official scoring Space

In that design:

  1. Use the official /questions response as the source of truth for the 20 task IDs.
  2. Match those task IDs to the corresponding GAIA validation rows.
  3. Use the row’s actual file_path.
  4. Download the attachment with hf_hub_download().
  5. Pass the resulting local path to the agent’s file-processing tools.
  6. Keep the file in the runtime cache; do not publish or mirror it.
  7. Submit only the generated answers through the official /submit route.

Do not replace the official question set with the first 20 GAIA validation rows. The course selection is a filtered set, and a locally selected first-20 subset may not match the task IDs accepted by the scoring endpoint.

This is still an unofficial workaround, but it avoids exposing a new file-serving endpoint.

Why /questions can work while /files fails

The question endpoint and the attachment endpoint depend on different layers.

/questions only needs metadata such as:

  • task_id;
  • question text;
  • level;
  • file_name.

The presence of a file_name in metadata does not mean that the corresponding binary file already exists on the Space filesystem.

The attachment flow additionally requires:

metadata file_path
    ↓
dataset-repository file
    ↓
authenticated download
    ↓
local cache path
    ↓
task_id mapping
    ↓
FileResponse

The current failure occurs in that metadata-to-local-file transition. This is why /questions can return the correct XLSX filename while /files/{task_id} still returns a mapping-related 404.

The Hugging Face Hub download API is already designed for this transition: hf_hub_download() retrieves one repository file, stores it in the Hub cache, and returns its local path.

Scope and limitations

A few distinctions are worth keeping clear:

  • I am not an HF maintainer, and this is not an official confirmation.
  • The PR being shown as “ready to merge” means that it is technically mergeable; it does not mean that it has been reviewed or approved.
  • The end-to-end validation was performed on a private duplicate of the current Space, not on the official production Space.
  • The official Space’s token and deployment configuration are not visible externally, so the maintainers still need to verify that configuration when merging.
  • Until the change is merged and the official Space is rebuilt, the public endpoint should still be treated as unfixed.
  • The PR deliberately does not include unrelated cleanup such as dependency pinning, loader refactoring, revision management, or broader error-response changes.

Those may be useful follow-up improvements, but they are not required to restore the attachment endpoint.

Useful references: