agentscope-ai/agentscope · error · ValueError

Not a local blob URI: {uri!r}

Error message

Not a local blob URI: {uri!r}

What it means

LocalBlobStore methods that accept a URI (open, size, delete, exists) first extract the key with _key_from_uri, which requires the URI to start with the 'local://' scheme. Anything else — s3:// URIs, bare keys, file paths — raises this ValueError, preventing cross-backend mix-ups.

Source

Thrown at src/agentscope/app/rag/blob_store/_local.py:78

        Args:
            key (`str`):
                Backend-relative key.

        Returns:
            `Path`:
                The absolute filesystem path within :attr:`_root`.
        """
        if not key or key.startswith("/") or ".." in Path(key).parts:
            raise ValueError(f"Invalid blob key: {key!r}")
        path = (self._root / key).resolve()
        if self._root not in path.parents and path != self._root:
            raise ValueError(f"Blob key {key!r} escapes the root directory.")
        return path

    def _key_from_uri(self, uri: str) -> str:
        """Extract the backend-relative key from a ``local://`` URI."""
        if not uri.startswith(_SCHEME):
            raise ValueError(f"Not a local blob URI: {uri!r}")
        return uri[len(_SCHEME) :]

    async def write_stream(self, key: str, stream: IO[bytes]) -> str:
        """Copy ``stream`` into the blob at ``key`` in 1 MiB chunks.

        Creates intermediate directories as needed.  Existing blobs at
        the same key are overwritten — keys are generated server-side
        from document ids, so collisions only happen on intentional
        re-uploads.

        Args:
            key (`str`):
                Backend-relative key.
            stream (`IO[bytes]`):
                Synchronous binary source.

        Returns:
            `str`:

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Use the URI returned by write_stream verbatim: uri = await store.write_stream(key, stream), then store.open(uri)
  2. Prefix bare keys: f'local://{key}'
  3. Select the right backend: keep an S3BlobStore for s3:// URIs and LocalBlobStore for local://
  4. Centralize backend dispatch on the URI scheme prefix

Example fix

# before
await store.open('data/abc.bin')  # ValueError: Not a local blob URI

# after
await store.open('local://data/abc.bin')
Defensive patterns

Strategy: validation

Validate before calling

SCHEME = 'local://'

def to_local_uri(key_or_uri: str) -> str:
    return key_or_uri if key_or_uri.startswith(SCHEME) else SCHEME + key_or_uri

uri = to_local_uri('data/abc.bin')
await store.open(uri)

Type guard

def is_local_uri(uri: str) -> bool:
    return uri.startswith('local://')

Try / catch

try:
    data = await store.open(uri)
except ValueError as e:
    if 'Not a local blob URI' in str(e):
        uri = 'local://' + uri  # or route to the correct backend
        data = await store.open(uri)
    else:
        raise

Prevention

When it happens

Trigger: Calling store.open('s3://bucket/x'), store.exists('data/abc.bin'), or any URI-form method on LocalBlobStore with a non-local:// string. Note the asymmetry: write_stream takes a bare key, while URI methods take local:// URIs.

Common situations: Code written against S3BlobStore reused with LocalBlobStore; passing a raw filesystem path; forgetting the scheme when copying URIs returned by write_stream (which returns a proper local:// URI — store and reuse it).

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/34404cd4b802149c. Report an issue: GitHub.