User-Facing Error Messages

errors specs/errors/user-facing-messages.kmd

Toda mensagem de erro exibida a usuário final deve ter (1) texto humanizado em pt-BR/en-US, (2) botão "Ver detalhes" expandindo o erro técnico bruto, (3) ID único `<PRODUCT>-<CAT>-<CODE>-<SEQ>` para correlação com logs e suporte. Aplica-se a apps mobile/desktop/web, landing pages e CLI.

When this pattern applies

Primary triggers

All triggers

Specification body

Spec — User-Facing Error Messages

Applicability

All Koder products that display errors to end users — mobile apps, desktop apps, web apps, web landing pages, CLI tools, and any product surface where a non-developer user can encounter an error.

Out of scope: server-side logs, developer-only consoles, internal admin dashboards (audience is technical and benefits from raw errors).


1. Required Anatomy

Every error displayed to a user must have three parts:

  1. Humanized message — short, clear, non-technical. States what happened and what the user can do. Localized: pt-BR for Brazilian users, en-US otherwise (see policies/language.kmd).
  2. "View details" affordance — a button, link, or expandable section labeled "Ver detalhes" (pt-BR) / "View details" (en-US). On activation, reveals the raw technical error (exception type, stack trace, HTTP status, response body).
  3. Error ID — a unique, copyable identifier shown in both the humanized message and the details panel. Format: <PRODUCT>-<CATEGORY>-<CODE>-<SEQ> (e.g., KSTORE-DL-503-001).

Forbidden: showing only the technical error to the user (e.g., Exception: BAD_DECRYPT(e_chacha20poly1305.cc:323)). Forbidden even as a fallback.


2. Humanized Message Rules

  • One sentence, max ~120 characters. If the user needs more, put it after the period.
  • Active voice, present tense. "Não foi possível baixar" not "O download falhou de ser possível".
  • Tell the user what to do when there's a clear next step ("Tente novamente em alguns segundos", "Verifique sua internet").
  • Never blame the user unless they actually caused it (typed wrong password = OK to say so; server bug ≠ user fault).
  • Never use jargon: avoid "token", "TLS", "handshake", "503", "exception", "stack trace", "endpoint", "API", "DNS", "MAC", "decrypt".
  • No emojis in the text itself (icon next to the message is OK if the product uses icons consistently).

Standard templates

Situation pt-BR en-US
Servidor temporariamente indisponível (5xx, timeout) "Servidor ocupado. Tente novamente em alguns segundos." "Server busy. Please try again in a few seconds."
Recurso não encontrado (404) "Conteúdo indisponível no momento." "Content unavailable right now."
Falha de rede / sem conexão "Sem conexão. Verifique sua internet e tente novamente." "No connection. Check your internet and try again."
Erro de TLS / handshake / decrypt "Conexão instável. Tente novamente." "Unstable connection. Please try again."
Espaço insuficiente "Espaço insuficiente no dispositivo." "Not enough space on device."
Permissão negada (auth) "Você precisa entrar na sua conta para continuar." "You need to sign in to continue."
Permissão negada (autorização) "Você não tem permissão para essa ação." "You don't have permission for this action."
Versão do app obsoleta "Atualize o aplicativo para continuar." "Please update the app to continue."
Dados inválidos enviados pelo usuário "Verifique os dados informados." "Please check the information entered."
Operação cancelada pelo usuário (não mostrar erro) (do not show error)
Erro desconhecido (fallback) "Algo deu errado. Toque em "Ver detalhes" se precisar reportar." "Something went wrong. Tap "View details" to report it."

Products may add product-specific templates, but the categories above are the baseline.


3. "View details" Panel

When the user activates the affordance, show:

  1. Error ID (large, copyable, prominent at the top)
  2. Timestamp in ISO 8601 with timezone (2026-04-17T15:42:30-03:00)
  3. Technical message — the original exception, stack trace, HTTP status + body, system call error, etc. Verbatim, no rewriting.
  4. Context — what the user was trying to do (e.g., "Downloading koder-hub v1.8.2 for android/arm64")
  5. App version and OS/platform (e.g., "Koder Hub 2.29.59 · Android 15 · arm64")
  6. Copy button that copies the entire details block as plain text, formatted so the user can paste into chat/email to support.

The details panel must be collapsed by default. Users should not see it unless they ask for it.


4. Error ID Format

<PRODUCT>-<CATEGORY>-<CODE>-<SEQ>

  • PRODUCT — uppercase product slug, max 8 chars (KSTORE, DEK, KMAIL, TALK, KORTEX)
  • CATEGORY — 2-letter functional area:
    • DL download · UP upload · AU auth · NW network · DB database/storage
    • UI UI render · IO filesystem · PM permission · VL validation · XX unknown
  • CODE — HTTP status (503, 404) for HTTP errors; otherwise a short numeric code defined by the product
  • SEQ — 3-digit zero-padded sequence to disambiguate multiple call sites with the same category+code (001, 002, ...)

Examples:

  • KSTORE-DL-503-001 — Store download token service unavailable
  • KSTORE-DL-404-001 — Store download file missing on storage
  • KSTORE-NW-000-001 — Store network connection failed (no HTTP response)
  • DEK-AU-401-001 — Dek user authentication required
  • KMAIL-UP-507-001 — Kmail attachment upload exceeded storage quota

Each product maintains its error-ID registry in <product>/docs/technical/errors.md. The registry lists every ID, its trigger condition, the humanized message used, and the call site (file:line).


5. Reporting Channel

The "Copy" button in the details panel formats the report as:

Erro: KSTORE-DL-503-001
Quando: 2026-04-17T15:42:30-03:00
App: Koder Hub 2.29.59
Sistema: Android 15 (arm64)
Fazendo: Baixando atualização do Koder Hub v1.8.2

Detalhes técnicos:
Exception: Failed to get download token (503): {"error":"Service Unavailable","message":"token service temporarily unavailable"}

Products that have an in-app feedback channel (Settings → Help → Report a problem) must pre-fill that channel with this block. Products without an in-app channel must show a "Copy" button that puts the block on the clipboard so the user can paste into Slack/email/WhatsApp/support form.


6. Implementation Notes

Flutter (mobile + desktop)

  • Centralize humanization in lib/core/error_humanizer.dart (or equivalent). Single function: HumanizedError humanize(Object exception, ErrorContext ctx).
  • All try/catch blocks that surface errors to UI must route through the humanizer. Direct SnackBar(content: Text(e.toString())) is forbidden.
  • The error display widget (ErrorBanner, ErrorDialog, etc.) is a single shared widget that takes a HumanizedError and renders the three parts (message, details affordance, ID).

Web apps

  • Centralize in src/errors/humanize.ts (or equivalent). Same single-function API.
  • The error UI component is a shared React/Vue component used everywhere errors surface.
  • The details panel uses a native <details> element or a styled disclosure widget — must be keyboard-accessible.

Backend

  • Backend services must include the error ID in their JSON error responses when possible:
    {"error_id": "KSTORE-DL-503-001", "error": "Service Unavailable", "message": "token service temporarily unavailable"}
    
  • The same error ID must appear in the server log line for that request, so support can correlate the user's report with the log.

CLI tools

  • CLIs print the humanized message to stderr.
  • A --verbose or -v flag (or simply the presence of $DEBUG) enables the technical details + error ID inline.
  • Without verbose, append a hint: Use --verbose para ver detalhes técnicos.

7. Audit Checklist (run by /k-housekeep)

For each product surface:

  1. Grep for raw exception display — flag any Text(e.toString()), print(e), console.error(e) that reaches user UI.
  2. Confirm shared humanizer existserror_humanizer.dart (Flutter) / humanize.ts (web) present and exported.
  3. Confirm shared error widget exists — single ErrorBanner/ErrorDialog component, no inline error UI scattered.
  4. Confirm error registry exists<product>/docs/technical/errors.md with at least one entry per known error ID.
  5. Confirm backend includes error_id in JSON error responses (when product has a backend).
  6. Confirm copy/report affordance present in the details panel.

Failures are listed in the housekeep report; products with violations cannot pass a release audit.


8. Migration Guidance

Existing products are exempt until their next release. On the next release:

  • Add the shared humanizer + error widget if missing.
  • Add error IDs starting at 001 per category as call sites are touched. Backfilling all call sites at once is not required — incremental adoption is fine, as long as new code follows the spec.
  • Each release note must mention which error categories now have humanized messages, so users notice the improvement.

References