fix(dynamicprompts): prevent hang on unknown wildcards#9307
fix(dynamicprompts): prevent hang on unknown wildcards#9307Pfannkuchensack wants to merge 4 commits into
Conversation
Referencing an unknown wildcard previously hung the combinatorial generator forever: its not-found fallback yields the wrapped wildcard infinitely, and the variant dedup logic discards those duplicates without ever advancing. This froze the UI prompt preview. Unknown wildcards are now detected up front and reported as a clear error instead of attempting generation.
lstein
left a comment
There was a problem hiding this comment.
The fix adds find_missing_wildcards() and short-circuits with an error before running the generators. The detection logic itself is sound (correct dynamicprompts API usage, and InvokeAI always uses an empty WildcardManager so treating every referenced wildcard as unresolvable is internally consistent). However, after running the generators in this venv (dynamicprompts 0.31.0), the guard is much broader than the actual failure condition, which introduces two regressions.
1. (High) The guard errors on prompts that never hang — the hang is specific to unknown wildcards used as variant values
find_missing_wildcards() flags any unknown wildcard, but only a wildcard nested inside a variant actually loops. Verified against CombinatorialPromptGenerator:
| Prompt | Combinatorial result |
|---|---|
a __nope__ b |
✅ ['a __nope__ b', ...] (works) |
__nope__ |
✅ ['__nope__', ...] (works) |
{__nope__|x} |
❌ HANG |
{__random__8chan|...} |
❌ HANG (the reported case) |
So a __nope__ b previously generated valid output, but now find_missing_wildcards("a __nope__ b") == ["nope"], so the router attaches an error and the invocation raises ValueError. Any prompt containing literal double-underscore text that isn't inside a variant (e.g. trigger-token style a photo, __my_style__) is now broken though it worked before.
Suggested fix: narrow detection to wildcards reachable as variant values (only flag a wildcard whose ancestor is a VariantCommand), rather than every wildcard in the tree.
2. (Medium) The guard fires regardless of combinatorial, but the random generator never hangs
utilities.py:60 and prompt.py:36 run the check unconditionally. The random generator handles unknown wildcards gracefully — verified: RandomPromptGenerator().generate("{__random__8chan|fenster|stuff}", ...) returns ['fenster', '__random__8chan', 'stuff'], no hang. So for combinatorial=False the guard is pure regression: it turns working output into an error.
This matters most in DynamicPromptInvocation, where combinatorial defaults to False (prompt.py:31) — the default code path now errors on prompts it used to handle.
Suggested fix: gate the check on if combinatorial: / if self.combinatorial:.
Things that are fine
parse()'sParseExceptionis swallowed and[]returned; the generators re-raise it and the router's existingexcept ParseExceptionstill handles malformed prompts. Consistent.- Variable-assignment wildcards (
${v=__nope__}${v}) are not traversed by_iter_wildcard_names, but they don't hang the combinatorial generator either, so that traversal gap is not a correctness hole. - New tests are correct and match current behavior.
Root cause / recommendation
Both findings stem from one root cause: the guard is broader than the bug. Narrowing detection to variant-nested wildcards and gating on combinatorial would fix the hang without regressing the several classes of previously-working prompts (including the invocation's default mode). Tests should be extended to cover a __nope__ b (combinatorial → no error) and the random-generator path.
…ariant values Only an unknown wildcard used as a variant value hangs the combinatorial generator; a bare `a __nope__ b` and the random generator both handle unknown wildcards fine. Restrict detection to variant-nested wildcards and gate the check on the combinatorial path so previously-working prompts no longer error.
Summary
Referencing an unknown wildcard previously hung the combinatorial generator forever: its not-found fallback yields the wrapped wildcard infinitely, and the variant dedup logic discards those duplicates without ever advancing. This froze the UI prompt preview. Unknown wildcards are now detected up front and reported as a clear error instead of attempting generation.
This is the minimal fix extracted from #9290 — it contains only the hang fix, none of the wildcard-file feature (no
wildcards_dirsetting, noWildcardManagerfile loading).Related Issues / Discussions
Replaces #9290
QA Instructions
{__random__a|b|c}.No values found for wildcard(s): random.The same guard applies to the
DynamicPromptnode, which now raises a clearValueErrorinstead of hanging the invocation.Automated tests:
Merge Plan
Standard merge — no special handling required. Changes are additive and backwards-compatible:
No new config settings.
No DB schema or redux slice changes.
No generated docs/settings changes.
Checklist
What's Newcopy (if doing a release after this PR)