GH GambleHub

Error handling and UX explanations

1) Why it matters

An error is not a "red text," but a continuation of the script. Good UX errors:
  • explains what happened and what to do next,
  • saves the entered data and prevents loss of progress,
  • gives a safe repeat or alternative path,
  • remains available (SR/keyboard) and does not reveal too much.

2) Error typology (for interface)

1. Data validation (4xx client): empty/invalid fields, format, length, rule conflict.
2. Business rules: limits, geo-constraints, KYC/KYB, duplicates, unavailable slots.
3. Rights/permissions: role, access to the resource, age restrictions.
4. Network/server: timeout, offline, 5xx, overload, rate limit.
5. Conflicts/status: 409/412 (data changed), races, locks.
6. No resource: 404/410, deleted/transferred.
7. Payment and risk: rejection by the bank/PSP, anti-fraud, limits of responsible play.

3) Channels and display level

We select the "volume" for the context:
ChannelWhenExample
Inline at the fieldValidation/Hint"Minimum 8 characters"
Under block/formStep error"Failed to save. Try again"
Toast (role = status)Non-blocking events"File loaded with errors, details below"
Banner on pageImportant but not blocking navigation"Offline. Showing cached data"
Modalka (role = alertdialog)Blocking risky steps"The session has expired. Sign in again"
Error page404/5xx Critical Drops"Page not found "/" Sorry, the failure is on our side"

Rule: Do not hide critical in toast/hover. Where the user is watching, there is a message.

4) Copywriting errors

Structure: cause → effect → action.
Tone: honest, neutral, guilt-free.
Specifics: specify a field/condition, avoid codes and stacks.

Button-action: "Repeat," "Change card," "Reset filters," "Open chat."

Sensitive data: do not show (PAN masking, personal attributes).

Examples

Good: "Payment failed: the bank declined the transaction. Try another method or try again later.
Bad: "Error 500. Something went wrong".

Good: "The daily spending limit has been reached. Set a new limit or try tomorrow."

Good: "The file is too large (max. 25 MB). Please compress or download some files.

5) Behavior and focus (A11y)

The error is displayed in the focus context: we transfer the focus to the first erroneous field.
Living regions: 'role = "status"' (polite) for info, 'role = "alert"' (assertive) for critical.
Visible ': focus-visible', contrast ≥ AA, alternatives to color (icon/text).
We bind the message to the field via 'aria-descripedby'.

html
<label for = "pwd "> Password </label>
<input id="pwd" name="password" aria-describedby="pwd-err" aria-invalid="true">
<p id = "pwd-err" role = "alert"> Minimum 8 characters </p>

6) Retrai, backoff and idempotency

Repetition is offered if there is a chance of success (network failures, 5xx, rate limit).

Exponential backoff 1-2-4-8 s, limit of attempts, understandable button "Repeat."

Critical transactions (rates/payments): mandatory Idempotency-Key → exclude duplicates.
Rolling back optimistic updates - clear visual return + clarification.

js async function retry(fn, attempts=3){
let wait=1000; for(let i=0; i<attempts; i++){
try{ return await fn(); }catch(e){ if(i===attempts-1) throw e; await new Promise(r=>setTimeout(r,wait)); wait=2; }
}
}

7) Offline, timeouts and partial content

Offline: we show the "No connection" banner, access to the cache (read-only), synchronization queue.
Timeouts: UI timeout (3-5 seconds) → status "Waiting for confirmation..." with safe redo/undo.

Partial success: we keep what we managed; marking "not synchronized."

8) Conflicts and competitiveness

409/412: data out of date. Suggest "Update" and show the diff (which has changed).
Locks: we inform who holds the block, and how long, the "Request access" button.

9) Sample UI templates

Page Banner:
html
<div class="banner banner--error" role="alert">
<strong> Connection failed. </strong> Shows cached data.
<button class =" btn btn--ghost" id = "retry "> Retry </button>
</div>
Critical error modal:
html
<div role="alertdialog" aria-labelledby="err-title" aria-describedby="err-desc">
<h2 id = "err-title "> Session expired </h2>
<p id = "err-desc "> Sign in again to continue. </p>
<button class = "btn "> Sign in </button>
<button class =" btn btn--ghost"> Home </button>
</div>
React ErrorBoundary (with correlation ID):
tsx function Fallback({ id, onRetry }: { id: string; onRetry: ()=>void }) {
return (
<div role="alert" className="banner banner--error">
<strong> We couldn't load the page. </strong>
<div> Try again. Код: <code>{id}</code> <button onClick={()=>navigator. clipboard. writeText (id)}> Copy </button> </div>
<button onClick = {onRetry}> Retry </button>
</div>
);
}

10) Error tokens (design system)

json
{
"error": {
"tones": { "danger": "#", "warning": "#", "info": "#" },
"aria": { "polite": true, "assertive": true },
"timing": { "toastMs": 3500, "retryBackoffMs": [1000,2000,4000] },
"layout": { "fieldGap": 8, "bannerIcon": 20 }
}
}
CSS presets:
css
.banner--error { background: var(--bg-danger); color: var(--on-danger); padding: 12px 16px; border-radius: 12px; }
.field-error { color: var(--role-danger); margin-top: 6px; font-size:.875rem; }

11) Security and privacy

We do not display stack traces, internal IDs, database paths.
We mask sensitive values ​ ​ (maps, documents).
Messages should not prompt an attacker (for example, that an account exists).
For support - correlation ID instead of parts.

Structured log (backend):
json
{"level":"error","event":"payment_fail","correlation_id":"c-8f1...","user_id":"u-","route":"/pay","psp_code":"DO_NOT_EXPOSE_TO_USER"}

12) Metrics and Control

INP and the share of Long Tasks at the time of the error (the error should not "hang" the UI).
Retry success rate, errors per 1000 actions, time to restore.
CTR on "Help/Chat," percentage of forms dropped.
Heat maps: where field-errors occur most often.

13) QA checklist

Availability

  • Focus on first invalid field; 'aria-describedby '/' aria-invalid' set.
  • Critical messages - 'role = "alert"'; contrast ≥ AA.

Behavior

  • Form data is not lost on error.
  • There is a clear'Retry 'and a correct backoff.
  • Offline mode/cache working; banner see.

Copywriting

  • Reason → Action; without technical jargon and accusations.
  • Texts are localized and do not break the grid.

Security

  • No PII leak/secrets; show only secure codes/ID.
  • Idempotency is enabled for critical operations.

14) The specifics of iGaming

Rate:
  • UI immediately records' busy '; at delay> 3 s - "Waiting for confirmation...."
  • At fail: honest status ("market closed," "coefficient has changed") + safe 'Retry'.
  • Idempotent key to eliminate double bid.
Payment/Withdrawal:
  • We distinguish between "bank failure/PSP" vs "server failure." For the first - "Choose another method," for the second - 'Retry'.
  • Transparent KYC/AML steps; links "Why is it necessary? ».
Responsible play and limits:
  • The tone is caring, no pressure. "Limit reached - pause or update the limit."
  • No outbreaks/neon; AAA contrast, availability at SR.
Geo/License:
  • Clearly explain the restrictions and suggest "Read the rules/support."

15) Anti-patterns

"Something went wrong" without action and context.
Resets the form after an error.
Hide critical in toast for 3 seconds.
Only color without text/icon.
Endless retreats without the possibility of cancellation.
Show internal codes/stack trails.

16) Documentation in the design system

Компоненты: `FieldError`, `FormError`, `PageBanner`, `AlertDialog`, `ErrorBoundary`.
Tone/contrast/timing tokens, a11y presets, and ARIA examples.
Map of typical scenarios (validation, network, rights, payments) with text templates.
"Do/Don't": real before/after screens with failure/success metrics.

Brief Summary

Make mistakes understandable and manageable: speak human language, save entered data, offer safe repetition and alternatives, respect accessibility and privacy. Then even emergency situations retain confidence and do not interrupt the user's path - especially in critical scenarios of bets and payments.

Contact

Get in Touch

Reach out with any questions or support needs.We are always ready to help!

Start Integration

Email is required. Telegram or WhatsApp — optional.

Your Name optional
Email optional
Subject optional
Message optional
Telegram optional
@
If you include Telegram — we will reply there as well, in addition to Email.
WhatsApp optional
Format: +country code and number (e.g., +380XXXXXXXXX).

By clicking this button, you agree to data processing.