Solution skill
Classify and fix Streamlit cache failures (st.cache_data / st.cache_resource)
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
Documentlists, 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_resourcereturns the SAME global object to every session;@st.cache_datareturns 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 sharedcache_resourceresult.
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_resourcereturn values as read-only singletons. If code must transform the result, copy first (df.copy(),copy.deepcopy), or move the mutable working copy intost.session_stateper user.Never cache per-user values with the global caches. A
load_for_user(user_id)under@st.cache_datashares memory across all sessions, per-user state belongs inst.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
Hashing crash → name the argument from the error;
_-prefix it, add ahash_funcsentry, or remove it from the signature.Stale data → the key doesn't watch the outside world; add
ttlor a per-function.clear()refresh path.Cross-session corruption → you're mutating a
cache_resourcesingleton; copy before mutating, or move the mutable state tost.session_state.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>