A prioritized list of ideas to keep improving how FalkorDB Browser explains query errors. Ideas are ordered top (highest value, do first) → bottom, so the team can plan and implement them in sequence. Each idea is written to be clear even if you've never touched this code before.
Every idea below follows the same template:
- Problem today — what a user experiences now.
- The idea — the change, in plain language.
- Example — a concrete before → after so the value is obvious.
- Why it's valuable — the payoff.
- How it would work — grounded notes pointing at real files.
- Impact / Effort / Depends on / Risks.
When you run a Cypher query that fails, FalkorDB Browser already:
- Shows the database's error message instead of a cryptic blob.
- For recognized errors, adds a short 💡 hint that tells you how to fix it.
- Shows that message + hint in three places: the error toast, the query‑history tooltip, and the editor hover (for syntax errors).
- Offers a "See more" toggle to reveal the exact raw database error.
The hint logic lives in lib/cypherErrors.ts (a small catalog of ~16 known errors
mapped to fixed, static tips: getCypherErrorHint(raw) returns { id, hint } or
undefined). It's wired into the UI through lib/utils.ts,
components/ui/toaster.tsx, app/components/PaginationList.tsx, and
app/components/CypherEditor.tsx.
Example of today's behavior
You run: MATCH (n) WITH n.name RETURN n.name
Toast:
Error
WITH clause projections must be aliased
💡 Every expression in a WITH clause must be aliased with AS,
e.g. WITH count(n) AS total.
See more ▸
That's good. The ideas below make it great.
- Impact — how much it helps users (Low / Medium / High).
- Effort — rough build cost (S = hours, M = a day or two, L = multi‑day).
- Depends on — anything that should ideally come first.
Impact: High · Effort: S–M · Depends on: nothing · Risk: low
When you misspell a function name or variable, we explain the category of mistake but not the specific fix. You still have to spot the typo yourself.
The browser knows the valid function names and the variables you defined in your query. When the database reports an unknown function or undefined variable, compare the bad name against those lists (using an edit‑distance "closest match" algorithm) and suggest the real one. Only suggest when the match is close, so we never guess wildly.
This idea covers the two cases that actually produce a database error:
| You typed | We compare against | Suggestion |
|---|---|---|
RETURN lenght("hi") → Unknown function 'lenght' |
function candidates | "Did you mean length()?" |
MATCH (person) RETURN prsn → 'prsn' not defined |
variables bound in the query | "Did you mean person?" |
Note: misspelling a label like
MATCH (:Persn)does not cause an error — it simply matches nothing. So label/property suggestions are a proactive editor warning, not an error hint; they belong with idea #2 (editor linting), not here.
You run: RETURN lenght("hello")
Before:
Error
Unknown function 'lenght'
💡 Check the function name for typos. See the FalkorDB function reference…
After:
Error
Unknown function 'lenght'
💡 Did you mean length()?
This is the single biggest jump from "the computer says I'm wrong" to "the computer hands me the fix." Typos are the most common mistake, and the suggestion is often the exact text the user needs to paste back in — zero thinking required.
- Function candidates: the editor already has a static
FUNCTIONSarray inapp/components/CypherEditor.tsx, and also pulls remote graph functions + UDFs for autocomplete. Use those candidate sources (the static list alone may be incomplete). - Variables:
CypherEditor.tsxalready extracts the variables bound in the query (it does this to color them). Reuse that set. - Add a small
closestMatch(name, candidates)helper (Levenshtein distance, with a max‑distance threshold so weak matches are dropped). - Compute the suggestion where the error is shown — it can stay out of the pure
lib/cypherErrors.tscatalog (which intentionally has no app dependencies) and layer on top of the catalogid. - Plumbing to be aware of: the toast/history layer currently receives only the
raw error/status/hint, not the failed query text or the candidate lists. Variable
suggestions in particular need the query in scope, so this requires passing the
failed query + candidates into the error‑presentation layer (or computing the
enriched suggestion before calling
toast()).
Impact: High · Effort: L · Depends on: shared token‑extraction (pairs with #1) · Risk: medium
Only syntax errors get highlighted in the editor (via Monaco decorations). For everything else the error appears only in a toast, away from the code — so you must mentally map the message back to a spot in your query.
Split editor feedback into two honest tiers, because not every error has a precise location:
- Token‑local squiggles — for errors we can pin to a token (unknown
function, undefined variable, a locatable missing
AS): underline that exact token and put the hint in its hover. - Whole‑query diagnostics — for errors with no single location (timeout, out‑of‑memory, write‑queue‑full, constraint violation, a type mismatch we can't localize): keep showing these as the toast / history hint / a small banner above the editor — not a fake token marker.
Use Monaco markers (setModelMarkers) for the squiggles + hover. A VS Code‑style
"Problems panel" is not built into standalone Monaco, so treat that (and
keyboard navigation between errors) as an optional extra we'd build ourselves.
You run: MATCH (n) WITH n.name RETURN n.name
Before: error only in a toast; the editor shows nothing.
After (token‑local squiggle in the editor):
MATCH (n) WITH n.name RETURN n.name
~~~~~~
└─ hover: "WITH projections must be aliased.
💡 Add AS, e.g. WITH n.name AS name"
People read errors fastest when the error sits on the line that caused it. This removes the "ok, but where?" step and makes the editor feel like a real IDE — while being honest that some errors are query‑wide.
- Today
CypherEditor.tsxuses Monaco decorations for syntax errors only. AddsetModelMarkersfor proper squiggles + marker hovers. - For token‑local non‑syntax errors there's no position from the server, so locate
the offending token in the query text (e.g., find
lenght, or the undefined variable name) and place the marker there. This token‑finding helper is the same one idea #1 needs — build it once, share it. - Bonus (proactive lint): since we have the graph schema (
Labels,PropertyKeysinapp/api/graph/model.ts), we can warn about unknown labels / properties even though they don't error — e.g., a faint underline on:Persnwith "No label:Persnexists. Did you mean:Person?" - Stretch — Quick Fixes: Monaco's
registerCodeActionProvidercan offer one‑click fixes: addAS nameto an unaliasedWITH; add direction->to an undirectedCREATE; wrap a value withtoInteger(...).
- Token‑finding can mis‑locate when the same name appears twice; prefer the first occurrence and fall back to a whole‑query diagnostic if unsure.
Impact: High · Effort: M–L · Depends on: explicit‑send UX + a dedicated AI prompt mode · Risk: medium (privacy)
Our catalog recognizes ~16 common errors. The other ~100+ possible database errors (and messy multi‑clause queries) fall back to the raw message with no tailored guidance.
Add a user‑initiated "Explain with AI" / "Fix with AI" action on the error toast and history item. On click, it sends your query + the exact error to an AI flow and returns a plain‑English explanation and/or a corrected query.
You run (an error our catalog does NOT recognize):
MATCH (p:Person) WHERE avg(p.age) > 30 RETURN p
Before:
Error
An unexpected error occurred. Please try again.
See more ▸ Invalid use of aggregating function 'avg' ← raw only; no tailored help
After (click ✨ Explain with AI):
"Aggregations like avg() can't be used inside a WHERE clause. Here's one
way to fix it — compute the average first, then compare:
MATCH (p:Person)
WITH avg(p.age) AS avgAge
MATCH (p:Person) WHERE p.age > avgAge
RETURN p"
No static catalog can cover every error or every weird query. The AI fills the gap for the rare/complex cases — and because we feed it the real error, the answer is specific to your query, not generic docs.
- The app already has an AI chat (
app/graph/Chat.tsx) and chat API (app/api/chat/route.tsx) built on@falkordb/text-to-cypher. However, that flow is oriented toward "turn a question into Cypher" and validates graph state — so this likely needs a distinct prompt mode / endpoint for "explain or fix this failed query," not just passing extra fields through the existing flow. - When we do have a catalog hint, include it in the AI prompt so the AI's answer stays grounded and consistent with our other messaging.
- Only send on an explicit user click — never auto‑send a failed query.
- Respect the app's configured AI/provider settings (the query + error may go to an external model). Gate the button's visibility on those settings.
Impact: Medium (compounding) · Effort: S–M · Depends on: nothing · Risk: low (with redaction)
We're guessing which errors are most common, and we don't know which ones fall through to the generic message in the wild.
Count how often each recognized error fires, and — crucially — capture a privacy‑safe fingerprint of the unrecognized ones so we know what to add next.
The trick that makes this safe and useful: before recording an unrecognized error, redact only the user‑controlled parts (quoted identifiers, numbers, string literals) while keeping the public, non‑sensitive keywords (function names, the error‑template words). We learn the shape of the error — including which function/feature it's about — without storing anyone's data.
Raw unrecognized error (contains user data — never stored as‑is):
Invalid input for function 'date': '2026-13-40'
Stored fingerprint (only the user literal is redacted — 'date' is a public
keyword, so we keep it):
Invalid input for function 'date': '<value>' ×137 this week
Action it drives:
UNRECOGNIZED = 12% of all errors; 60% of those normalize to the
datetime template above → the next catalog entry to add is datetime parsing.
Turns the roadmap from opinion into data. Every later improvement (more hints, better suggestions) gets pointed exactly where users are actually getting stuck — without collecting raw queries or personal data.
getCypherErrorHint()already returns a stableid. Emit a count for thatid; forundefined, emit the redacted fingerprint instead.- Redaction is a small client‑side function: replace quoted tokens / numbers with placeholders, but keep a short allow‑list of public keywords (function names, error‑template words) so the fingerprint stays actionable. Record only counts + fingerprints, never the raw query.
- Optional: a "Was this hint helpful? 👍 👎" control to measure hint quality.
Impact: Medium · Effort: M · Depends on: #4 to prioritize; coordinate with #6 · Risk: low
The database defines ~120 error templates (EMSG_* in its error_msgs.h); we have
hints for ~16. New database versions can also reword a message and silently break
our text matching.
Two parts, deliberately staged to avoid wasted work:
- Now: add a few high‑frequency entries (driven by idea #4's data) — cheap, high‑value.
- Before investing in 100+ mappings: coordinate idea #6 (structured codes upstream). If stable codes are coming, we don't want to hand‑write and maintain a hundred fragile regexes.
Plus a drift guard: an automated test that runs known‑bad queries against a real FalkorDB and asserts each is still recognized — so a future reword fails CI, not the user.
Before (a real but unhandled error — falls through to the generic message,
with the raw text tucked behind "See more"):
Error
An unexpected error occurred. Please try again.
See more ▸ Type mismatch: list expected to have a single element, but had 3
After (new catalog entry — the real message is shown, plus a hint):
Error
Type mismatch: list expected to have a single element, but had 3
💡 This spot expects a single value but got a list. Use a function like
head(list) or an index list[0], or aggregate it first.
More coverage = more users get a helpful hint. The drift guard means the feature keeps working as FalkorDB evolves instead of quietly rotting.
- Add entries to
lib/cypherErrors.tsin the same anchored‑pattern style, each citing itsEMSG_*source. - The drift guard mirrors the manual check done during development: spin up a
FalkorDB container, fire the bad queries, assert
getCypherErrorHint()matches. Gate it as an opt‑in CI job (like the existing Playwright e2e).
Impact: Medium–High · Effort: L · Depends on: upstream coordination · Risk: medium (cross‑repo)
We recognize errors by matching English text (e.g., the words "not defined"). This is fragile (a reworded message breaks it) and English‑only (blocks translation).
Match on a stable error code instead of words — the way Postgres uses SQLSTATE codes. Two possible routes:
- Port the hint catalog upstream into the
falkordbJavaScript client (mirroring what falkordb‑rs already did withmitigation_hint()in PR #248), so the mapping lives in one place and every JS app benefits; or - Have the database return a structured error code alongside the message.
The browser then keeps only its unique, context‑aware layer (the "Did you mean?" suggestions from idea #1).
Before (brittle, English‑only):
if (message.includes("not defined")) { /* show hint */ }
After (stable, translatable):
if (error.code === "VAR_NOT_DEFINED") { /* show localized hint */ }
Robustness and reuse: messages can change wording without breaking us, the logic isn't duplicated across projects, and translation becomes possible. This is the "do it properly" foundation that ideas #5 and #7 lean on.
Impact: Medium · Effort: S–M · Depends on: #6 makes i18n much cleaner · Risk: low
Hints are English‑only static strings, and a screen‑reader user may hear only "Error" without the helpful detail.
- i18n: move hint text into a translatable message catalog so non‑English users get hints in their language (stable error codes from #6 make this clean).
- Accessibility: announce the hint and wire up the "See more" toggle properly —
add
aria-controlslinking the toggle to the raw‑error region, and anaria-liveregion so the hint is read aloud when the toast appears. (We already addedaria-expanded; this finishes the job.)
i18n:
Before (always English): 💡 Check the function name for typos.
After (Spanish locale): 💡 Comprueba si el nombre de la función es correcto.
Accessibility (what a screen reader announces):
Before: "Error."
After: "Error. Unknown function 'lenght'. Hint: did you mean length?"
Reaches more users (non‑English speakers, screen‑reader users), so the error help works for everyone, not just some.
- Replace literal hint strings with keys resolved through an i18n layer; the
getCypherErrorHintidis a natural translation key. - In
components/ui/toaster.tsx, give the raw‑error block anid, point the toggle at it viaaria-controls, and wrap the description in anaria-live="assertive"region.
Impact: Low–Medium · Effort: S each · Depends on: nothing
Small, self‑contained polish items that can slot in between larger efforts. Each has a quick before → after so the payoff is clear:
- Syntax hint in the toast too — today the generic syntax hint shows only on
editor hover.
Before:syntax toast has no 💡. ·After:💡 Check for typos, missing commas, or unbalanced brackets/quotes near the highlighted spot. - Merge the one‑off mapping — fold the existing special‑case for "exactly one
relationship type" into the standard catalog so every error flows through one
path.
Before:two code paths. ·After:one catalog entry (easier to test). - "Retry" button for truly transient errors only — limit to write‑queue‑full
/ network blips (with backoff).
Before:user re‑types and re‑runs. ·After:one‑click Retry. (Note: do NOT offer Retry for timeout/out‑of‑memory — those are usually deterministic and retrying just repeats an expensive query; prefer "simplify / add LIMIT / raise timeout".) - Deep‑link the fix — the query‑timeout hint links to the timeout field in
Settings.
Before:"raise the timeout in settings." ·After:"Raise it in Settings →" (a real link). - "Learn more →" links — each hint optionally links to the matching FalkorDB docs
page.
Before:hint only. ·After:hint + "Learn more →". - Copy raw error — a copy button next to "See more" for easy bug reports.
Before:select‑and‑copy by hand. ·After:one‑click copy.
| # | Idea | Impact | Effort | Why this order |
|---|---|---|---|---|
| 1 | "Did you mean?" suggestions | High | S–M | Biggest UX win; data already in‑app |
| 2 | Errors in the editor (markers + quick fixes) | High | L | IDE‑grade; shares token‑finding with #1 |
| 3 | Explain / Fix with AI | High | M–L | Covers the long tail; needs privacy + prompt mode |
| 4 | Telemetry on error ids + fingerprints | Medium | S–M | Makes all later work data‑driven |
| 5 | More coverage + drift guard | Medium | M | More hits; stage with #6 to avoid rework |
| 6 | Structured error codes | Med–High | L | Robust, reusable, enables i18n |
| 7 | i18n & accessibility | Medium | S–M | Reach everyone; cleaner after #6 |
| 8 | Low‑hanging fruit | Low–Med | S | Quick polish, anytime |
Recommended first step: Idea #1 (function‑name "Did you mean?"), because the function candidates are already in the editor and the payoff is immediate and obvious. It also builds the shared token‑finding helper that idea #2 reuses.