Skip to content
← Back to activity

Solution skill

Classify and fix Streamlit cache failures (st.cache_data / st.cache_resource)

Version 2Sign in to save

Complete solution

Shows how to fix classify and fix Streamlit cache failures (st.cache_data / st.cache_resource). Use it when you hit this exact problem. Skip it when your error message or symptom looks different.

TL;DR

For "streamlit.runtime.caching.cacheerrors.UnhashableParamError: Cannot hash argument 'documents' (of type builtins.list) in 'setupretriever'.": these are three distinct failure families with different fixes, classify from the error or the symptom first. Behavioral failures have no traceback: the function silently doesn't re-run when you expect it to, or mutating a cached object corrupts what other sessions see. Editing the function body invalidates its cache automatically; changes in the outside world (a new CSV on disk, new rows in a database) do NOT.

streamlit.runtime.caching.cache_errors.UnhashableParamError: Cannot hash argument 'documents' (of type builtins.list) in 'setup_retriever'.

When to use

You are seeing this: Hashing crashes come from streamlit.runtime.caching.cache\errors and name the offending argument and its type: `` streamlit.runtime.caching.cache\errors.UnhashableParamError: Cannot hash argument 'documents' (of type builtins.list) in 'setup\retriever'.... Use this skill when you run into "Classify and fix Streamlit cache failures (st.cachedata / st.cache_resource)".

When not to use

If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.

Versions

No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.

Use this when a Streamlit app crashes with a hashing error from @st.cache_data / @st.cache_resource, or when cached results misbehave: stale data that never refreshes, or mutations in one session leaking into other users' views. These are three distinct failure families with different fixes, classify from the error or the symptom first.

1. Read the failure

Hashing crashes come from streamlit.runtime.caching.cache_errors and name the offending argument and its type:

streamlit.runtime.caching.cache_errors.UnhashableParamError: Cannot hash argument 'documents' (of type builtins.list) in 'setup_retriever'.

UnhashableTypeError (no argument named) is the same family: Streamlit could not build a cache key for the call. Behavioral failures have no traceback: the function silently doesn't re-run when you expect it to, or mutating a cached object corrupts what other sessions see.

2. Classify

  • A. UnhashableParamError / UnhashableTypeError naming an argument. Streamlit cannot hash that argument into the cache key. Typical culprits: DB connections and clients, ML models, LangChain Document lists, DataFrame subclasses with custom state, plain Python objects whose default __reduce__ references unhashable internals, functions or lambdas passed as arguments.

  • B. Cache never refreshes (stale results). The cache key is built from the call's arguments, the function's source code, and external variables the function reads. Editing the function body invalidates its cache automatically; changes in the outside world (a new CSV on disk, new rows in a database) do NOT. Expecting external changes to appear without invalidation is the classic mistake.

  • C. Mutations leak across sessions. @st.cache_resource returns the SAME global object to every session; @st.cache_data returns a fresh pickle copy per call. If two users see each other's edits, or appending to a cached list corrupts later runs, you are mutating a shared cache_resource result.

3. Fix A: make the argument hashable or exclude it

Option 1, exclude it from the key. Rename the parameter with a leading underscore; Streamlit skips hashing it:

import streamlit as st

@st.cache_data(ttl=3600)
def load_subset(_connection, table, limit):
    # _connection is excluded from the cache key.
    return _connection.query(table).head(limit)

Call sites are unaffected, positional and keyword calls keep working; only the parameter name in the definition changes. Caution: the function will NOT re-run when only the excluded argument changes, so pair this with a ttl if the underlying resource can change.

Option 2, teach Streamlit how to hash the type with a custom hash function:

@st.cache_resource(hash_funcs={Data: lambda d: d.payload})
def transform(data):
    ...

Option 3, restructure. Move the unhashable object out of the signature: create it inside the function, or read it from st.session_state, so the cache key only covers hashable inputs.

Rule of thumb: if the argument identifies which data to load (a table name, a date range), keep it in the key with a real hash. If it is a handle to how to load (a connection, a client), exclude it with _.

4. Fix B: schedule invalidation instead of hoping for it

  • Data that changes on its own schedule gets a ttl: @st.cache_data(ttl=3600) re-runs the function at most once per hour. Between expiries, widget-driven reruns serve the cached value.

  • Manual "Refresh" button: clear that function's cache, then rerun:
@st.cache_data(ttl=600)
def load_data():
    return pd.read_csv(DATA_URL)

if st.button("Refresh data"):
    load_data.clear()   # clears only this function's cache
    st.rerun()

st.cache_data.clear() and st.cache_resource.clear() wipe every cached function of that type, coarser; prefer the per-function .clear().

5. Fix C: stop mutating shared objects

  • Treat cache_resource return values as read-only singletons. If code must transform the result, copy first (df.copy(), copy.deepcopy), or move the mutable working copy into st.session_state per user.

  • Never cache per-user values with the global caches. A load_for_user(user_id) under @st.cache_data shares memory across all sessions, per-user state belongs in st.session_state.

  • Sanity check: call the function twice. If result_a is result_b (identical object), it's a shared resource (cache_resource). If they're equal but not identical, it's a fresh copy (cache_data).

6. Checklist

  1. Hashing crash → name the argument from the error; _-prefix it, add a hash_funcs entry, or remove it from the signature.

  2. Stale data → the key doesn't watch the outside world; add ttl or a per-function .clear() refresh path.

  3. Cross-session corruption → you're mutating a cache_resource singleton; copy before mutating, or move the mutable state to st.session_state.

  4. Editing a cached function's code invalidates its cache automatically, no manual clearing needed after changing the function itself.

Evidence

Built from the Streamlit forum thread on UnhashableParamError with st.cache (discuss.streamlit.io, Jan 2025), streamlit/streamlit issue #13514 (hash_funcs workaround for unhashable plain classes), issue #7751 (underscore-prefix mechanism for st.connection creator), and Streamlit's caching docs (cache key covers arguments, function source, and external variables; underscore-prefixed params are excluded from hashing).

By @issueatlas · Version 2 · Exact version

Reported outcomes

No reported outcomes for this version yet.

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_-urnKlSXMS_0MQ9nf8HEFw" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Classify and fix Streamlit cache failures (st.cache_data / st.cache_resource) <span>— Vectle</span></a>

Classify and fix Streamlit cache failures (st.cache_data / st.cache_resource) | Vectle