{"record":{"id":"49f507c9404e11ea","repo":"BerriAI/litellm","slug":"prompt-characters-must-be-provided-for-tts-calls","errorCode":null,"errorMessage":"prompt_characters must be provided for tts calls. prompt_characters={prompt_characters}, model={model}, custom_llm_provider={custom_llm_provider}, call_type={call_type}","messagePattern":"prompt_characters must be provided for tts calls\\. prompt_characters=(.+?), model=(.+?), custom_llm_provider=(.+?), call_type=(.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/cost_calculator.py","lineNumber":495,"sourceCode":"    \"\"\"\n    if model_with_provider in model_cost_ref:  # Option 2. use model with provider, model = \"openai/gpt-4\"\n        model = model_with_provider\n    elif model in model_cost_ref:  # Option 1. use model passed, model=\"gpt-4\"\n        model = model\n    elif (\n        model_without_prefix in model_cost_ref\n    ):  # Option 3. if user passed model=\"bedrock/anthropic.claude-3\", use model=\"anthropic.claude-3\"\n        model = model_without_prefix\n\n    # see this https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models\n    if call_type == \"speech\" or call_type == \"aspeech\":\n        speech_model_info = litellm.get_model_info(model=model_without_prefix, custom_llm_provider=custom_llm_provider)\n        cost_metric: Final = select_cost_metric_for_model(speech_model_info)\n        prompt_cost: float = 0.0\n        completion_cost: float = 0.0\n        if cost_metric == \"cost_per_character\":\n            if prompt_characters is None:\n                raise ValueError(\n                    f\"prompt_characters must be provided for tts calls. prompt_characters={prompt_characters}, model={model}, custom_llm_provider={custom_llm_provider}, call_type={call_type}\"\n                )\n            _prompt_cost, _completion_cost = _generic_cost_per_character(\n                model=model_without_prefix,\n                custom_llm_provider=custom_llm_provider,\n                prompt_characters=prompt_characters,\n                completion_characters=0,\n                custom_prompt_cost=None,\n                custom_completion_cost=0,\n            )\n            if _prompt_cost is None or _completion_cost is None:\n                raise ValueError(\n                    f\"cost for tts call is None. prompt_cost={_prompt_cost}, completion_cost={_completion_cost}, model={model_without_prefix}, custom_llm_provider={custom_llm_provider}, prompt_characters={prompt_characters}, completion_characters={completion_characters}\"\n                )\n            prompt_cost = _prompt_cost\n            completion_cost = _completion_cost\n        elif cost_metric == \"cost_per_token\":\n            prompt_cost, completion_cost = generic_cost_per_token(","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/cost_calculator.py#L477-L513","documentation":"For text-to-speech calls priced per character (e.g. OpenAI TTS models in LiteLLM's cost map), the calculator needs the input length. If call_type is 'speech'/'aspeech', the model's cost metric resolves to 'cost_per_character', and prompt_characters is None, it raises ValueError instead of guessing a cost.","triggerScenarios":"Invoking litellm.speech()/aspeech and then completion_cost (usually via logging) without prompt_characters; calling completion_cost(call_type='speech') on a model whose model_info uses cost_per_character while omitting the character count.","commonSituations":"Custom wrappers that compute TTS costs from a response object only; models newly switched to per-character pricing in a LiteLLM upgrade; forgetting that TTS responses carry audio, not token usage.","solutions":["Pass prompt_characters=len(input_text) to completion_cost for speech calls.","Let LiteLLM's own logging path compute it — call the speech API through the standard litellm.speech(...) wrapper rather than computing cost manually.","Update LiteLLM if an older version failed to thread prompt_characters through its logging object.","For per-token-priced TTS models no characters are needed; confirm which metric applies via litellm.get_model_info(model)['cost_metric'] or the model's pricing keys."],"exampleFix":"# before\ncost = litellm.completion_cost(\n    model=\"tts-1\", call_type=\"speech\", prompt=\"hello world\",\n)\n\n# after\ncost = litellm.completion_cost(\n    model=\"tts-1\", call_type=\"speech\", prompt=\"hello world\",\n    prompt_characters=len(\"hello world\"),\n)","handlingStrategy":"validation","validationCode":"if call_type in (\"speech\", \"aspeech\"):\n    info = litellm.get_model_info(model=model_without_prefix, custom_llm_provider=custom_llm_provider)\n    if \"input_cost_per_character\" in (info or {}):\n        assert prompt_characters is not None, \"pass prompt_characters=len(text) for per-character TTS pricing\"\n        prompt_characters = prompt_characters if prompt_characters is not None else len(input_text)","typeGuard":"def tts_cost_ready(model: str, text: str, prompt_characters: int | None) -> bool:\n    if prompt_characters is not None:\n        return True\n    try:\n        info = litellm.get_model_info(model=model) or {}\n        return \"input_cost_per_character\" not in info\n    except Exception:\n        return False","tryCatchPattern":"try:\n    cost = litellm.completion_cost(model=model, call_type=\"speech\", prompt=text, prompt_characters=len(text))\nexcept ValueError as e:\n    if \"prompt_characters must be provided\" in str(e):\n        cost = litellm.completion_cost(model=model, call_type=\"speech\", prompt=text, prompt_characters=len(text))\n    else:\n        raise","preventionTips":["For any TTS cost call, always pass prompt_characters=len(input_string).","Prefer litellm's built-in logging for speech calls instead of manual cost computation.","Write a helper that computes TTS cost so the character count is never forgotten."],"tags":["litellm","cost-calculation","tts","speech","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}