-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
Describe the bug
The /v1/archives POST endpoint (create archive) crashes with an AttributeError when using the embedding parameter. The endpoint code incorrectly tries to access .provider and .model attributes on a string value.
Error:
AttributeError: 'str' object has no attribute 'provider'
Please describe your setup
- How are you running Letta?
- Docker (
letta/letta:0.14.0)
- Docker (
- Describe your setup
- OS: macOS (Darwin 25.1.0)
- Docker command:
docker compose upwith the following configuration:letta: image: letta/letta:0.14.0 ports: - 8283:8283 environment: LETTA_PG_DB: letta LETTA_PG_USER: letta LETTA_PG_PASSWORD: letta LETTA_PG_HOST: pgvector_db LETTA_PG_PORT: 5432 ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY GEMINI_API_KEY: $GEMINI_API_KEY
Screenshots
Full stack trace:
letta-1 | Letta.letta.server.rest_api.middleware.logging - ERROR - Unhandled exception in request: AttributeError: 'str' object has no attribute 'provider'
letta-1 | Traceback (most recent call last):
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 151, in call_next
letta-1 | message = await recv_stream.receive()
letta-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
letta-1 | File "/app/.venv/lib/python3.11/site-packages/anyio/streams/memory.py", line 126, in receive
letta-1 | raise EndOfStream from None
letta-1 | anyio.EndOfStream
letta-1 |
letta-1 | During handling of the above exception, another exception occurred:
letta-1 |
letta-1 | Traceback (most recent call last):
letta-1 | File "/app/letta/server/rest_api/middleware/logging.py", line 81, in dispatch
letta-1 | response = await call_next(request)
letta-1 | ^^^^^^^^^^^^^^^^^^^^^^^^
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 159, in call_next
letta-1 | raise app_exc
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 144, in coro
letta-1 | await self.app(scope, receive_or_disconnect, send_no_error)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
letta-1 | await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
letta-1 | raise exc
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
letta-1 | await app(scope, receive, sender)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/routing.py", line 716, in __call__
letta-1 | await self.middleware_stack(scope, receive, send)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/routing.py", line 736, in app
letta-1 | await route.handle(scope, receive, send)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/routing.py", line 290, in handle
letta-1 | await self.app(scope, receive, send)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/routing.py", line 78, in app
letta-1 | await wrap_app_handling_exceptions(app, request)(scope, receive, send)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
letta-1 | raise exc
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
letta-1 | await app(scope, receive, sender)
letta-1 | File "/app/.venv/lib/python3.11/site-packages/starlette/routing.py", line 75, in app
letta-1 | response = await f(request)
letta-1 | ^^^^^^^^^^^^^^^^
letta-1 | File "/app/.venv/lib/python3.11/site-packages/fastapi/routing.py", line 302, in app
letta-1 | raw_response = await run_endpoint_function(
letta-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
letta-1 | File "/app/.venv/lib/python3.11/site-packages/fastapi/routing.py", line 213, in run_endpoint_function
letta-1 | return await dependant.call(**values)
letta-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
letta-1 | File "/app/letta/server/rest_api/routers/v1/archives.py", line 68, in create_archive
letta-1 | handle = f"{archive.embedding.provider}/{archive.embedding.model}"
letta-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
letta-1 | AttributeError: 'str' object has no attribute 'provider'
Additional context
- This affects Letta version 0.14.0
- Model used:
letta/letta-free(free embedding model) - The deprecated
embedding_configparameter still works but requires constructing a fullEmbeddingConfigobject - The bug prevents using the simpler string-based
embeddingparameter that was intended as the replacement
Root Cause
In letta/server/rest_api/routers/v1/archives.py:
# Line 30: embedding is defined as a string
class ArchiveCreateRequest(BaseModel):
embedding: Optional[str] = Field(None, description="Embedding model handle for the archive")
# Lines 67-68: Code incorrectly treats it as an object
if embedding_config is None and archive.embedding is not None:
handle = f"{archive.embedding.provider}/{archive.embedding.model}" # ❌ Bug!The embedding field is defined as Optional[str] (line 30), but the code on line 68 tries to access .provider and .model attributes as if it were an object.
Steps to Reproduce
Using the Letta Python client:
from letta_client import Letta
client = Letta(base_url="http://localhost:8283", token="your_token")
# This triggers the bug
archive = client.archives.create(
name="test-archive",
description="Test archive",
embedding="letta/letta-free"
)Expected Behavior
The archive should be created successfully using the string handle "letta/letta-free".
Suggested Fix
Since archive.embedding is already a string handle in the format "provider/model" (e.g., "letta/letta-free"), the code should use it directly:
Change line 68 in letta/server/rest_api/routers/v1/archives.py from:
handle = f"{archive.embedding.provider}/{archive.embedding.model}"to:
handle = archive.embeddingFull corrected code block (lines 66-72):
embedding_config = archive.embedding_config
if embedding_config is None and archive.embedding is not None:
handle = archive.embedding # ✅ Fix: use the string directly
embedding_config = await server.get_embedding_config_from_handle_async(
handle=handle,
actor=actor,
)