Error handling patterns.
AllExceptionsFilter runs only when the exception leaves the handler. Returning
responseService.error(...) from a catch consumes it, so Nest answers HTTP 200
with the real status buried in the body. Any client that reads the status line sees a
success where the backend rejected.
// WRONG — responds 200 with {"success":false,…,"statusCode":409}
try {
const result = await this.tablesService.update(id, dto);
return this.responseService.updated(result, 'Mesa actualizada exitosamente');
} catch (error) {
return this.responseService.error(error.message, error.status || 400);
}
// RIGHT — no try/catch; the service throws typed and the filter emits 409 + error_code
const result = await this.tablesService.update(id, dto);
return this.responseService.updated(result, 'Mesa actualizada exitosamente');
Precondition before deleting a try/catch: every path of the service must throw
VendixHttpException or a Nest HttpException. Verify with
grep -c "throw new Error" <service> — it must be 0, otherwise the filter degrades
the case to SYS_INTERNAL_001 / 500, which is worse than the 200.
Reference implementations with zero try/catch:
apps/backend/src/domains/store/tables/tables.controller.ts and its sibling
table-sessions.controller.ts.
Measured 2026-07-30: 358 responseService.error calls across 54 controllers
still carry the pattern, plus 16 frontend reads of success === false compensating
for it. Repo-wide sweep ticket: QUI-571.
Deliberate exception — do not "fix" it. apps/backend/src/domains/auth/auth.controller.ts
answers 200 with statusCode: 401 on failed login, and
apps/frontend/src/app/core/store/auth/auth.effects.ts:193,488 reads that body on
purpose. Changing it breaks login.
Never hand-roll an envelope unwrapper (if (res.success === false) throw ...). It hides
a broken contract instead of reporting it, and the filter's error body carries
"success": null — not false — so the check silently stops matching the moment the
status starts travelling correctly. Rely on catchError + extractApiErrorMessage.
apps/backend/src/common/errors/error-codes.ts.apps/backend/src/common/errors/vendix-http.exception.ts.apps/backend/src/common/filters/http-exception.filter.ts.apps/frontend/src/app/core/utils/error-messages.ts.apps/frontend/src/app/core/utils/parse-api-error.ts and api-error-handler.ts.Prefer VendixHttpException with an existing ErrorCodes entry:
throw new VendixHttpException(ErrorCodes.PAYMENT_SOURCE_NOT_FOUND, undefined, { payment_source_id });
The registry contains mixed naming styles. Do not invent a stricter format than the current file; follow nearby domain naming.
Responses include:
statusCodeerror_codemessagetimestamppathdetailsdevDetailsValidation arrays are mapped by AllExceptionsFilter to SYS_VALIDATION_001. Unknown errors map to SYS_INTERNAL_001.
Use extractApiErrorMessage(error) for simple display. It delegates to parseApiError() when error_code exists and maps to ERROR_MESSAGES.
Use parseApiError() directly only when component behavior depends on the code:
const { errorCode, userMessage } = parseApiError(error);
this.toastService.error(userMessage);
Never display backend developer details to users.
error-codes.ts near the owning domain.VendixHttpException at the service/controller boundary.error-messages.ts if the error can reach UI.details safe for clients; put sensitive diagnostics only in logs/dev details.vendix-validationvendix-backend-apivendix-frontendnpx skills add Rzyfront/vendix-error-handling下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer