BerriAI/litellm · error · ValueError
Invalid response from transcription provider, expected Trans
Error message
Invalid response from transcription provider, expected TranscriptionResponse, but got {type(response)} What it means
After awaiting the transcription call (covering cached dicts, direct TranscriptionResponse objects, and coroutines), litellm validates that the final result is a TranscriptionResponse. Anything else — str, a dict that failed coercion, None, or a custom object — triggers this ValueError, meaning the handler violated the return contract.
Source
Thrown at litellm/main.py:7576
# Add the context to the function
ctx: Final = contextvars.copy_context()
func_with_context: Final = partial(ctx.run, func)
_, custom_llm_provider, _, _ = get_llm_provider(model=model, api_base=kwargs.get("api_base", None))
# Await normally
init_response: Final = await loop.run_in_executor(None, func_with_context)
if isinstance(init_response, dict):
response = _transcription_response_from_cached_dict(init_response)
elif isinstance(init_response, TranscriptionResponse): ## CACHING SCENARIO
response = init_response
elif asyncio.iscoroutine(init_response):
response = await init_response
else:
# Call the synchronous function using run_in_executor
response = await loop.run_in_executor(None, func_with_context)
if not isinstance(response, TranscriptionResponse):
raise ValueError(
f"Invalid response from transcription provider, expected TranscriptionResponse, but got {type(response)}"
)
# Store duration in _hidden_params for cost calculation without
# exposing it in the response body. Adding duration to the response
# tricks the OpenAI SDK's "best match deserialization" into thinking
# a plain Transcription is a TranscriptionVerbose/Diarized type.
if response is not None and not isinstance(response, Coroutine) and file is not None:
existing_duration: Final = getattr(response, "duration", None)
if existing_duration is None:
calculated_duration: Final = calculate_request_duration(file)
if calculated_duration is not None:
response._hidden_params["audio_transcription_duration"] = calculated_duration
return response
except Exception as e:
custom_llm_provider = custom_llm_provider or "openai"
raise exception_type(View on GitHub (pinned to 77b7c6c40c)
Solutions
- Return litellm.TranscriptionResponse(text=..., ...) from custom transcription handlers
- If a logger/hook short-circuits the call, return a proper TranscriptionResponse per the current API
- Reproduce with litellm.set_verbose = True and print type(response) in the handler
- Upgrade litellm if your handler follows the latest docs
Example fix
# before
class MySTT(CustomLLM):
def transcription(self, **kwargs):
return "hello world" # raw string -> ValueError
# after
class MySTT(CustomLLM):
def transcription(self, **kwargs):
return TranscriptionResponse(text="hello world") Defensive patterns
Strategy: type-guard
Validate before calling
# For custom transcription handlers, wrap before returning
from litellm.types.utils import TranscriptionResponse
def to_transcription_response(raw) -> TranscriptionResponse:
if isinstance(raw, TranscriptionResponse):
return raw
if isinstance(raw, dict):
return TranscriptionResponse(**raw)
return TranscriptionResponse(text=str(raw)) Type guard
from litellm.types.utils import TranscriptionResponse
def is_transcription_response(resp: object) -> bool:
return isinstance(resp, TranscriptionResponse) Try / catch
try:
resp = litellm.transcription(model=model, file=f)
except ValueError as e:
if "expected TranscriptionResponse" in str(e):
# custom handler/cache broke the return contract
raise
raise Prevention
- Always construct typed response objects in custom handlers instead of returning raw strings/dicts
- Add unit tests asserting isinstance(handler.transcription(...), TranscriptionResponse)
When it happens
Trigger: A CustomLLM transcription handler returning a raw string/dict instead of TranscriptionResponse; a logging hook or cache returning an incompatible object; a mocked/monkeypatched transcription function in tests.
Common situations: Writing a custom transcription provider and forgetting to wrap the result; a caching layer returning serialized JSON that no longer coerces; litellm version drift changing the expected type.
Related errors
- Unsupported provider config: {transcription_provider_config}
- api_base must be provided for Hosted VLLM rerank
- Unmapped provider passed in. Unable to get the response.
- You must be a LiteLLM Enterprise user to use this feature. I
- custom_ui_sso_sign_in_handler is not configured. Please set
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/13b7461c58ff8a4d.
Report an issue: GitHub.