#23278·llama_index

[Bug]: MediaResource raises KeyError instead of ValidationError when path fails validation

Author: SletchCreated Sep 27, 2026Updated Sep 28, 2026

Bug Description

MediaResource.validate_mimetype reads info.data["path"] with a subscript:

python
# llama-index-core/llama_index/core/schema.py:589
rpath: str | None = info.data["path"]

In pydantic, a field that failed validation is absent from info.data. So when path is invalid, this subscript raises KeyError before pydantic can assemble the ValidationError.

The sibling lookup in the same validator, six lines above, already guards itself:

python
b64_data = info.data.get("data")       # line 583, guarded
...
rpath: str | None = info.data["path"]  # line 589, not guarded

So the two lines are inconsistent with each other. This reads as an oversight rather than a decision.

Expected pydantic.ValidationError, naming the path field and its type error
Actual KeyError: 'path', raised from inside the validator

Why it matters, beyond the exception type:

  1. The caller cannot except ValidationError, which is the documented way to handle a bad model.
  2. The real reason for the failure never reaches them. KeyError: 'path' reads like an internal bug in llama-index, not like "you passed the wrong type".

Version

llama-index-core 0.14.25. Reproduced on main at 169e450aa, Python 3.12 and 3.10, Linux.

Steps to Reproduce

python
from pathlib import PurePosixPath
from llama_index.core.schema import MediaResource

MediaResource(path=12345)
MediaResource(path=object())
MediaResource(path=PurePosixPath("a/b.txt"))
call raised expected
MediaResource(path=12345) KeyError: 'path' ValidationError
MediaResource(path=object()) KeyError: 'path' ValidationError
MediaResource(path=PurePosixPath("a/b.txt")) KeyError: 'path' ValidationError

The PurePosixPath case is the one most likely to be hit by accident. It looks like a path and reads like one, but FilePath does not accept it, so the failure surfaces as a KeyError about a dictionary the caller has never heard of.

Relevant Logs/Tracebacks

    @classmethod
    def validate_mimetype(cls, v: str | None, info: ValidationInfo) -> str | None:
        if v is not None:
            return v

        # Since this field validator runs after the one for `data`
        # then the contents of `data` should be encoded already
        b64_data = info.data.get("data")
        if b64_data:  # encoded bytes
            decoded_data = base64.b64decode(b64_data)
            if guess := filetype.guess(decoded_data):
                return guess.mime

        # guess from path
>       rpath: str | None = info.data["path"]
                            ^^^^^^^^^^^^^^^^^
E       KeyError: 'path'

llama_index/core/schema.py:589: KeyError

The fix

One word, making line 589 match line 583:

diff
-        rpath: str | None = info.data["path"]
+        rpath: str | None = info.data.get("path")

rpath is already annotated str | None and the very next line is if rpath:, so a None from a failed or absent field is already handled. Nothing downstream changes.

Tests

Added to llama-index-core/tests/schema/test_media_resource.py.

test pins fails without the change
test_invalid_path_raises_validation_error an invalid path surfaces as ValidationError for all three types above yes

Watched in both directions, at 169e450aa, Python 3.12:

result
the test alone, before the fix 1 failed, 6 passed, with KeyError: 'path' at schema.py:589
the same test, after the fix 7 passed

Collateral

Same tree, llama-index-core, in a container mirroring unit_test.yml:

surface before after
core, py3.12 1499 passed, 22 skipped, 6 xfailed 1500 passed, 22 skipped, 6 xfailed
core, py3.10 1499 passed, 22 skipped, 6 xfailed 1500 passed, 22 skipped, 6 xfailed
warnings, py3.12 2128 2126, none new
warnings, py3.10 2114 2114, unchanged
llama-dev test --cov --cov-fail-under=50 passed passed
pre-commit on the changed files n/a all hooks pass
mypy passed passed

There are no pre-existing failures on either interpreter, so the plus one is the new test and nothing else moved.

Scope

In scope: the single unguarded lookup at line 589, and a test for it.

Out of scope, deliberately:

  • The url serialization and validation in the same class. Not affected, since path is the only field read out of info.data by subscript.
  • MediaResource.hash embedding the OS path separator. That is #23058, which already has two open PRs, and I am not sending a third.
  • MediaResource.serialize_path (line 603) and ImageDocument.image_path (line 1372), which carry the same separator fault as #23058 and are not covered by either of its PRs. Reported in a comment on #23058 rather than here, because they are a different mechanism from this one.
  • Any change to what FilePath accepts. Making PurePosixPath valid would be a feature decision, not a bug fix, and it is not what this issue asks for.

Definition of done

  • MediaResource(path=<invalid>) raises ValidationError.
  • A named test covers it and fails without the change.
  • The core suite is unchanged apart from that test, on 3.12 and on 3.10.
  • No new warnings.

I have the change and the test ready and green on all of the above. Happy to open the PR, or to leave it if you would rather fold it into something larger.

AI-assisted, disclosed per CONTRIBUTING.md. Every measurement above is output from a command that was run.