harry0703/MoneyPrinterTurbo · error · ValueError
selectbox options cannot be empty: {key}
Error message
selectbox options cannot be empty: {key} What it means
stable_selectbox() is a WebUI helper that wraps Streamlit selectbox with stable business values as options (to survive reruns and language switches). It raises ValueError when the options list is empty, because a selectbox with zero options cannot render or keep state. The empty list almost always means an upstream data source (e.g. available voices, songs, providers) produced nothing at render time.
Source
Thrown at webui/Main.py:1630
def localized_widget_key(name, *parts):
# 部分 Streamlit selectbox 使用稳定 key 记住选择状态,但展示文本来自 locale。
# 语言切换时把语言也放进 key,可以强制重建控件,避免选中项仍显示旧语言。
language = st.session_state.get("ui_language", config.ui.get("language", ""))
suffix_parts = [name, language, *[str(part) for part in parts if part]]
return "_".join(suffix_parts)
def stable_selectbox(label, options, default_value, key, format_func=None, **kwargs):
# Streamlit 1.59 对 selectbox 的状态复用更敏感:如果控件没有固定 key,
# 或者真实选项只是一组临时下标,页面 rerun 后容易被重新计算的 index 覆盖,
# 表现为用户第一次选择不生效、需要再选一次。这个 helper 统一用稳定业务值
# 作为真实选项,并在 session_state 里保存该值;展示文案只通过 format_func
# 转换,避免翻译文案、选项顺序或上游配置变化影响选择状态。
options = list(options)
if not options:
raise ValueError(f"selectbox options cannot be empty: {key}")
if default_value not in options:
default_value = options[0]
widget_key = localized_widget_key(key)
selected_value = st.session_state.get(widget_key)
accepts_custom_value = bool(kwargs.get("accept_new_options"))
has_valid_custom_value = (
accepts_custom_value
and isinstance(selected_value, str)
and bool(selected_value.strip())
)
if selected_value not in options and not has_valid_custom_value:
# 如果上游选项发生变化(例如切换 TTS provider 后声音列表变了),
# 旧值已经不合法。控件创建前直接初始化 session_state,之后只让 key
# 管理状态,不再同时传入 index。这样可以避免 Streamlit 在 rerun 时
# 用重新计算的 index 覆盖用户刚选择的值,导致第一次选择不生效。
st.session_state[widget_key] = default_valueView on GitHub (pinned to 1f9f19c202)
Solutions
- Check why the upstream list is empty: e.g. for song/BGM pickers ensure resource/songs or storage/bgm actually contains readable files
- Supply a sensible non-empty fallback or default option list at the call site instead of passing a possibly-empty generator result
- If empty is legitimately possible, guard the call site: skip rendering the selectbox (and show an informative message) when options is empty rather than calling the helper
Example fix
# before
stable_selectbox(tr("Background Music"), songs, songs[0] if songs else None, key="bgm_song")
# after
if not songs:
st.info(tr("No background music available. Upload a song first."))
else:
stable_selectbox(tr("Background Music"), songs, songs[0], key="bgm_song") Defensive patterns
Strategy: type-guard
Validate before calling
options = list(options)
if not options:
st.info("No options available for this control yet.")
return # skip rendering instead of raising Type guard
def has_select_options(options) -> bool:
"""Narrow before calling stable_selectbox."""
return bool(list(options or [])) Try / catch
try:
value = stable_selectbox(label, options, default_value, key=key)
except ValueError as exc:
if "selectbox options cannot be empty" in str(exc):
st.warning(f"{label}: no options available")
value = None
else:
raise Prevention
- Materialize generator/list options with list(...) and truth-check before every stable_selectbox call
- Keep seed data present (e.g. at least one built-in song) so option lists are never empty on fresh installs
- Log a warning with the widget key whenever an options source comes back empty, to catch config drift early
When it happens
Trigger: Any call stable_selectbox(label, options=[], ...) — e.g. the list of BGM songs from storage/bgm + resource/songs is empty, a provider list filters to zero entries, or a config directory yields no items — while the page is being rendered.
Common situations: Fresh install with resource/songs and storage/bgm empty so the BGM picker has no candidates; a config file or directory the options derive from is missing/renamed; a filter (language, provider availability) removes every option; environment where a listing API returns nothing.
Related errors
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/65264ecd2c9ae0ef.
Report an issue: GitHub.