litellm - 💡(How to fix) Fix [Bug]: OpenTelemetry integration crashes with "unhashable type: 'list'" when guardrail mode is a list [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

12:34:01 - LiteLLM Proxy:ERROR: common_request_processing.py:1730 - litellm.proxy.proxy_server._handle_llm_api_exception(): Exception occured - unhashable type: 'list' Traceback (most recent call last): File "litellm/litellm/proxy/proxy_server.py", line 8337, in chat_completion result = await base_llm_response_processor.base_process_llm_request( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...<16 lines>... ) ^ File "litellm/litellm/proxy/common_request_processing.py", line 1360, in base_process_llm_request response = await proxy_logging_obj.post_call_success_hook( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...<3 lines>... ) ^ File "litellm/litellm/proxy/utils.py", line 2276, in post_call_success_hook raise e File "litellm/litellm/proxy/utils.py", line 2270, in post_call_success_hook callback_response = await callback.async_post_call_success_hook( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ user_api_key_dict=user_api_key_dict, data=data, response=response ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "litellm/litellm/integrations/opentelemetry.py", line 745, in async_post_call_success_hook self._create_guardrail_span(kwargs=kwargs, context=ctx) ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "litellm/litellm/integrations/opentelemetry.py", line 1483, in _create_guardrail_span if not self._emit_once( ~~~~~~~~~~~~~~~^ kwargs, ^^^^^^^ ...<3 lines>... guardrail_information.get("guardrail_mode"), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ): ^ File "litellm/litellm/integrations/opentelemetry.py", line 897, in _emit_once if spans_logged.get(dedupe_key) is True: ~~~~~~~~~~~~~~~~^^^^^^^^^^^^ TypeError: unhashable type: 'list' INFO: 127.0.0.1:54978 - "POST /v1/chat/completions HTTP/1.1" 500 Internal Server Error

Fix Action

Fixed

Code Example

from __future__ import annotations

from typing import Literal
from typing_extensions import override

from litellm.integrations.custom_guardrail import (
    CustomGuardrail,
    log_guardrail_information,
)
from litellm.types.utils import GenericGuardrailAPIInputs
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj

class MyCustomGuardrail(CustomGuardrail):
    @log_guardrail_information
    @override
    async def apply_guardrail(
        self,
        inputs: GenericGuardrailAPIInputs,
        request_data: dict,
        input_type: Literal["request", "response"],
        logging_obj: LiteLLMLoggingObj | None = None,
    ) -> GenericGuardrailAPIInputs:
        return inputs

---

model_list:
  # Configure any model.

guardrails:
  - guardrail_name: "custom-guardrail"
    litellm_params:
      guardrail: custom_guardrail.MyCustomGuardrail
      default_on: true
      mode: ["pre_call", "post_call"]

litellm_settings:
    callbacks: ["otel"]

---

curl -X POST 'http://localhost:4000/v1/chat/completions' \
  --header 'Accept: */*' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "model": "gemini-flash",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "hello"
        }
      ]
    }
  ]
}'

---

12:34:01 - LiteLLM Proxy:ERROR: common_request_processing.py:1730 - litellm.proxy.proxy_server._handle_llm_api_exception(): Exception occured - unhashable type: 'list'
Traceback (most recent call last):
  File "litellm/litellm/proxy/proxy_server.py", line 8337, in chat_completion
    result = await base_llm_response_processor.base_process_llm_request(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<16 lines>...
    )
    ^
  File "litellm/litellm/proxy/common_request_processing.py", line 1360, in base_process_llm_request
    response = await proxy_logging_obj.post_call_success_hook(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<3 lines>...
    )
    ^
  File "litellm/litellm/proxy/utils.py", line 2276, in post_call_success_hook
    raise e
  File "litellm/litellm/proxy/utils.py", line 2270, in post_call_success_hook
    callback_response = await callback.async_post_call_success_hook(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        user_api_key_dict=user_api_key_dict, data=data, response=response
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "litellm/litellm/integrations/opentelemetry.py", line 745, in async_post_call_success_hook
    self._create_guardrail_span(kwargs=kwargs, context=ctx)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "litellm/litellm/integrations/opentelemetry.py", line 1483, in _create_guardrail_span
    if not self._emit_once(
           ~~~~~~~~~~~~~~~^
        kwargs,
        ^^^^^^^
    ...<3 lines>...
        guardrail_information.get("guardrail_mode"),
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ):
    ^
  File "litellm/litellm/integrations/opentelemetry.py", line 897, in _emit_once
    if spans_logged.get(dedupe_key) is True:
       ~~~~~~~~~~~~~~~~^^^^^^^^^^^^
TypeError: unhashable type: 'list'
INFO:     127.0.0.1:54978 - "POST /v1/chat/completions HTTP/1.1" 500 Internal Server Error
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

When a guardrail that implements apply_guardrail (e.g. CrowdStrike AIDR) is configured with a list-valued mode (e.g. ["pre_call", "post_call"]) and the OpenTelemetry integration is active, every request that triggers the guardrail returns HTTP/500 with TypeError: unhashable type: 'list'.

Expected: The OpenTelemetry span deduplication should handle all valid guardrail_mode types without crashing.

git bisect points to https://github.com/BerriAI/litellm/commit/1c4e4d4a60e586b97ec4ab82c358291eaf8f07f2.

Steps to Reproduce

Custom guardrail (custom_guardrail.py):

from __future__ import annotations

from typing import Literal
from typing_extensions import override

from litellm.integrations.custom_guardrail import (
    CustomGuardrail,
    log_guardrail_information,
)
from litellm.types.utils import GenericGuardrailAPIInputs
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj

class MyCustomGuardrail(CustomGuardrail):
    @log_guardrail_information
    @override
    async def apply_guardrail(
        self,
        inputs: GenericGuardrailAPIInputs,
        request_data: dict,
        input_type: Literal["request", "response"],
        logging_obj: LiteLLMLoggingObj | None = None,
    ) -> GenericGuardrailAPIInputs:
        return inputs

Proxy config (config.yaml):

model_list:
  # Configure any model.

guardrails:
  - guardrail_name: "custom-guardrail"
    litellm_params:
      guardrail: custom_guardrail.MyCustomGuardrail
      default_on: true
      mode: ["pre_call", "post_call"]

litellm_settings:
    callbacks: ["otel"]

Request:

curl -X POST 'http://localhost:4000/v1/chat/completions' \
  --header 'Accept: */*' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "model": "gemini-flash",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "hello"
        }
      ]
    }
  ]
}'

Relevant log output

12:34:01 - LiteLLM Proxy:ERROR: common_request_processing.py:1730 - litellm.proxy.proxy_server._handle_llm_api_exception(): Exception occured - unhashable type: 'list'
Traceback (most recent call last):
  File "litellm/litellm/proxy/proxy_server.py", line 8337, in chat_completion
    result = await base_llm_response_processor.base_process_llm_request(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<16 lines>...
    )
    ^
  File "litellm/litellm/proxy/common_request_processing.py", line 1360, in base_process_llm_request
    response = await proxy_logging_obj.post_call_success_hook(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<3 lines>...
    )
    ^
  File "litellm/litellm/proxy/utils.py", line 2276, in post_call_success_hook
    raise e
  File "litellm/litellm/proxy/utils.py", line 2270, in post_call_success_hook
    callback_response = await callback.async_post_call_success_hook(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        user_api_key_dict=user_api_key_dict, data=data, response=response
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "litellm/litellm/integrations/opentelemetry.py", line 745, in async_post_call_success_hook
    self._create_guardrail_span(kwargs=kwargs, context=ctx)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "litellm/litellm/integrations/opentelemetry.py", line 1483, in _create_guardrail_span
    if not self._emit_once(
           ~~~~~~~~~~~~~~~^
        kwargs,
        ^^^^^^^
    ...<3 lines>...
        guardrail_information.get("guardrail_mode"),
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ):
    ^
  File "litellm/litellm/integrations/opentelemetry.py", line 897, in _emit_once
    if spans_logged.get(dedupe_key) is True:
       ~~~~~~~~~~~~~~~~^^^^^^^^^^^^
TypeError: unhashable type: 'list'
INFO:     127.0.0.1:54978 - "POST /v1/chat/completions HTTP/1.1" 500 Internal Server Error

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

v1.86.0-rc.1

Twitter / LinkedIn details

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING