MTG Cheaper Deck has four main stages where things can go wrong: fetching the deck from Moxfield, syncing or reading the local Scryfall database, matching deck cards against that database, and scoring candidates. Most errors are either a transient network block from Moxfield’s anti-bot layer, a missing or stale database, or a scoring edge case for highly unique synergy cards. The issues below cover every failure mode observed in normal use, with the exact error message and the steps to resolve it.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/invvd/mtg-cheaper-deck/llms.txt
Use this file to discover all available pages before exploring further.
Moxfield returned 403 (Cloudflare block)
Moxfield returned 403 (Cloudflare block)
Cause: Moxfield’s Cloudflare protection identified the request as automated and blocked it. The Fix: Wait a few minutes and try again — Cloudflare blocks are usually temporary. If the 403 persists, open Replace
fetch_deck_raw() function in moxfield_client.py raises a MoxfieldError with the message:moxfield_client.py and update the User-Agent string in DEFAULT_HEADERS to match a current browser version:Chrome/124.0.0.0 with the latest stable Chrome version number. Do not assume the API endpoint itself has changed unless a 403 persists after trying multiple User-Agent strings.Deck not found (404)
Deck not found (404)
Cause: The deck ID could not be resolved on Moxfield. This happens for two reasons: the URL is wrong, or the deck is set to Private on Moxfield.Fix:
fetch_deck_raw() raises:- Double-check the Moxfield URL. The deck ID is the alphanumeric segment at the end — for example, in
https://moxfield.com/decks/AbCdEf123, the ID isAbCdEf123. - Open the deck on Moxfield, go to deck settings, and confirm the visibility is set to Public. Private and unlisted decks cannot be fetched by the tool.
No such file or directory: ./data/cards.db
No such file or directory: ./data/cards.db
Cause: The local Scryfall database has never been created. The tool’s database layer looks for The script creates the
./data/cards.db (or the path set in DATABASE_URL), but sync_scryfall.py has not been run yet so neither the data/ directory nor the database file exist.Fix: Run the sync script once before launching any interface:data/ directory automatically, downloads Scryfall’s oracle-cards bulk data (~38 k cards), and populates the database. The full sync typically completes in under two minutes depending on your connection speed. After the first run, re-run the script once daily if you want up-to-date prices.RuntimeError: El objeto de bulk-data no trae 'jsonl_download_uri'
RuntimeError: El objeto de bulk-data no trae 'jsonl_download_uri'
Cause: Scryfall changed the shape of their bulk-data API response. The The error message already prints the actual keys present in the response.Fix: Visit
get_bulk_download_url() function in sync_scryfall.py looks for a jsonl_download_uri key in the JSON returned by https://api.scryfall.com/bulk-data/oracle-cards:https://api.scryfall.com/bulk-data/oracle-cards in a browser and inspect the returned JSON to find the correct download URL key. Then update get_bulk_download_url() in sync_scryfall.py to use that key instead of jsonl_download_uri.No suggestions returned for a card
No suggestions returned for a card
Cause: There are three independent reasons a card can come back with zero suggestions:For Simic (blue/green) commander decks, colorless artifact cards are always color-identity candidates. If those also appear in
- No price: The card has
price_usd = NULLin the local database, sosuggest_alternatives()returns[]immediately and the card appears inreport["no_price"]. - All candidates filtered out: Every card cheaper than the original fails the color identity check (bitwise AND mask), the type compatibility check, or the format legality check. This is common for mono-colored or colorless cards in a five-color deck where the original is a gold card.
- All candidates already in the deck: The
exclude_oracle_idsset passed tosuggest_alternatives()contains every oracle ID already on the board, so all matching candidates are skipped. The card appears inreport["no_suggestions"].
no_suggestions, the deck genuinely saturates the cheaper card pool for that type and there are no further options.All scores are below 1.5 (weak matches)
All scores are below 1.5 (weak matches)
Cause: The scored card is a synergy-specific card with no evergreen Scryfall keywords (aristocrat triggers, group slug effects, combo pieces, etc.). The scoring formula is:When
keywords is an empty list, keyword_similarity is 0.0. Highly distinctive oracle text means oracle_text_overlap is also near 0.0. The remaining two terms are capped at 1.0 each, making 1.5 roughly the ceiling for these cards even against a strong candidate.Fix: This is expected behavior, not a bug. Suggestions below 1.5 are still cheaper alternatives that passed the color identity, card type, and format legality filters — they share the same mechanical slot even if the rule text diverges. Evaluate them manually. The score thresholds from the README are a guide:| Range | Interpretation |
|---|---|
| 3.0 or above | Strong match — shared mechanics |
| 1.5 – 3.0 | Moderate match — some functional overlap |
| Below 1.5 | Weak match — same type/color/format only |
NotImplementedError: El filtro de legalities usa json_extract de SQLite
NotImplementedError: El filtro de legalities usa json_extract de SQLite
Cause: Fix: Update the Then remove or adjust the
DATABASE_URL is set to a PostgreSQL connection string, but load_candidate_pool() in scoring.py explicitly raises NotImplementedError for any non-SQLite dialect because the _POOL_SQL query uses SQLite’s json_extract() function:_POOL_SQL query in scoring.py to use PostgreSQL’s jsonb ->> operator:NotImplementedError guard. The color identity filter (bitwise AND on color_identity_mask) is already database-agnostic and requires no changes.Unmatched cards in the report
Unmatched cards in the report
Cause: A card name from the Moxfield deck has no matching row in the local database. The Common causes:
match_deck_cards() function in matching.py tries two lookup strategies — scryfall_id first, then case-insensitive name — and logs a warning for every card that fails both:- The local database is outdated and is missing a recently released card.
- The card is a token or emblem, which are not included in Scryfall’s
oracle-cardsbulk dataset. - The Moxfield deck uses an alternate card name (split card half, flip card face, etc.) that differs from the oracle name stored locally.
python sync_scryfall.py to refresh the database with the latest Scryfall data. If the card is a token or emblem it will never appear in the oracle-cards dataset — this is a known limitation and the card will always remain in the unmatched list.