Pular para o conteúdo

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.

Quando este padrão se aplica

Triggers primários

Todos os triggers

Corpo da especificação

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., HUB-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

Situationpt-BRen-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.


2.5 Rules (normative — Requirement/Scenario, RFC-013)

Destilação testável dos §1–§4 — sem mudança de significado normativo: cada cláusula abaixo já existia como prosa; a gramática a torna endereçável por id estável e geradora de TDD (1 Scenario = 1 teste, /k-test-gen FASE 1.5; gate de cobertura no /k-test FASE 4.5).

Requirement: Every user-facing error has the three-part anatomy {#req-error-anatomy}

Every error displayed to an end user SHALL carry the three parts of §1 — humanized message, "View details" affordance and copyable error ID — and the raw technical error SHALL NOT be shown alone (not even as fallback).

Scenario: Three parts present

  • GIVEN any error surfaced to an end user
  • WHEN the error UI renders
  • THEN a humanized message (≤ ~120 chars, no jargon) is shown
  • AND a "Ver detalhes" / "View details" affordance is present
  • AND a copyable error ID is visible

Scenario: Raw technical error never shown alone

  • GIVEN an exception with a technical message (e.g. BAD_DECRYPT(e_chacha20poly1305.cc:323))
  • WHEN it surfaces to the user
  • THEN the visible message is the humanized one
  • AND the raw text appears only inside the collapsed details panel

Requirement: Error ID follows the canonical format {#req-error-id-format}

Every error ID SHALL match <PRODUCT>-<CATEGORY>-<CODE>-<SEQ> (§4): uppercase product slug ≤8 chars, 2-letter category from specs/errors/taxonomy.kmd §R1, numeric code, 3-digit zero-padded sequence.

Scenario: Well-formed id accepted

  • GIVEN the id HUB-DL-503-001
  • WHEN it is validated against the format
  • THEN it parses into product HUB, category DL, code 503, seq 001

Scenario: Malformed id rejected

  • GIVEN an id with lowercase product, unknown category or missing seq (e.g. hub-ZZ-503)
  • WHEN it is validated
  • THEN validation fails naming the offending part

Requirement: Details panel is collapsed and complete {#req-details-panel}

The details panel SHALL be collapsed by default and, when opened, SHALL show the §3 fields (error ID, ISO-8601 timestamp, verbatim technical message, context, app version/platform) plus a copy affordance that emits the §5 report block.

Scenario: Collapsed by default

  • GIVEN an error UI just rendered
  • WHEN no user interaction happened
  • THEN the technical details are not visible

Scenario: Copy block carries the correlation fields

  • GIVEN the user activates the copy affordance
  • WHEN the clipboard content is inspected
  • THEN it contains the error ID, the ISO-8601 timestamp, app version/platform and the verbatim technical message

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 (HUB, DEK, KMAIL, TALK, KORTEX)
  • CATEGORY — 2-letter functional area. Canonical enum + semantic definitions + tie-break rules live in specs/errors/taxonomy.kmd §R1–R2 (this spec only consumes it):
    • 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:

  • HUB-DL-503-001 — Hub download token service unavailable
  • HUB-DL-404-001 — Hub download file missing on storage
  • HUB-NW-000-001 — Hub 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: HUB-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": "HUB-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.

Referências