Overview
The Crypto Shop Backend uses standard HTTP status codes and consistent error response formats. This page documents all error codes, their meanings, and how to handle them in your frontend application.
All errors follow a consistent JSON structure:
{
"error" : "Human-readable error message" ,
"code" : "MACHINE_READABLE_ERROR_CODE" ,
"details" : {
// Additional context-specific information
}
}
Standard Error Response
Human-readable error message describing what went wrong
Machine-readable error code for programmatic handling (optional)
Additional context-specific information (optional)
HTTP Status Codes
400 - Bad Request
The request is malformed or contains invalid data.
Common Scenarios:
Missing Required Fields
{
"error" : "Email and password are required"
}
Validation Errors
{
"errors" : [
{
"msg" : "Password must be at least 8 characters" ,
"param" : "password" ,
"location" : "body"
}
]
}
Insufficient Stock
{
"error" : "Insufficient stock for Crypto Hardware Wallet"
}
Insufficient Balance
{
"error" : "Insufficient balance" ,
"code" : "INSUFFICIENT_BALANCE" ,
"userBalance" : 50.0 ,
"requiredAmount" : 99.98
}
Error Code: INSUFFICIENT_BALANCE
Handling:
if ( error . code === 'INSUFFICIENT_BALANCE' ) {
const needed = error . requiredAmount - error . userBalance ;
alert ( `You need ${ needed . toFixed ( 2 ) } more TRX to complete this purchase.` );
}
Order Not Pending
{
"error" : "Order is not pending"
}
Wallet Not Found
{
"error" : "User wallet not found"
}
401 - Unauthorized
Authentication is required or has failed.
No Token Provided
{
"error" : "No token, authorization denied"
}
Location: src/middlewares/auth.js:8
Handling:
// Redirect to login page
if ( error . status === 401 ) {
window . location . href = '/login' ;
}
Invalid or Expired Token
{
"error" : "Token is invalid or expired"
}
Location: src/middlewares/auth.js:14
Handling:
// Attempt token refresh
if ( error . error === 'Token is invalid or expired' ) {
await refreshAccessToken ();
// Retry original request
}
Invalid Credentials
{
"error" : "Invalid email or password"
}
Location: src/api/auth/login.js:22
Account Inactive
{
"error" : "User account is inactive"
}
Location: src/api/auth/login.js:26
403 - Forbidden
Authentication succeeded but user lacks permission.
Admin Access Required
{
"error" : "Admin access only"
}
Location: src/middlewares/auth.js:25
Handling:
if ( error . status === 403 ) {
alert ( 'You do not have permission to access this resource.' );
router . push ( '/dashboard' );
}
User Access Required
{
"error" : "User access only"
}
Location: src/middlewares/auth.js:32
Unauthorized Access to Order
{
"error" : "Unauthorized"
}
Location: src/api/orders/payOrder.js:18
Returned when a user attempts to access or pay for another user’s order.
404 - Not Found
The requested resource does not exist.
Order Not Found
{
"error" : "Order not found"
}
Location: src/api/orders/payOrder.js:14
Product Not Found
{
"error" : "Product 507f1f77bcf86cd799439013 not found"
}
Location: src/api/orders/createOrder.js:34
Handling:
if ( error . status === 404 ) {
alert ( 'The requested resource was not found.' );
}
409 - Conflict
The request conflicts with the current state of the server.
Pending Order Exists
{
"error" : "You have a pending order. Please complete or cancel it first." ,
"code" : "PENDING_ORDER_EXISTS" ,
"pendingOrderId" : "507f1f77bcf86cd799439012"
}
Error Code: PENDING_ORDER_EXISTS
Location: src/api/orders/createOrder.js:16-20
Handling:
if ( error . code === 'PENDING_ORDER_EXISTS' ) {
const viewOrder = confirm ( 'You have a pending order. Would you like to view it?' );
if ( viewOrder ) {
router . push ( `/orders/ ${ error . pendingOrderId } ` );
}
}
Duplicate User
{
"error" : "Email already exists"
}
Returned when attempting to register with an existing email or username.
500 - Internal Server Error
An unexpected error occurred on the server.
Generic Server Error
{
"error" : "Internal server error message"
}
Handling:
if ( error . status === 500 ) {
alert ( 'An unexpected error occurred. Please try again later.' );
// Log error to monitoring service
logErrorToSentry ( error );
}
{
"error" : "Merchant wallet not configured"
}
Location: src/api/orders/createOrder.js:54
This indicates a server configuration issue. Contact support.
Transaction Failed
{
"error" : "Transaction broadcast failed: [blockchain error message]"
}
Location: src/api/orders/payOrder.js:80-83
Returned when the blockchain transaction fails. The order status is automatically set to failed.
Application-Specific Error Codes
Authentication & Authorization
Code Status Description Location INVALID_CREDENTIALS401 Email or password is incorrect auth/login.js ACCOUNT_INACTIVE401 User account has been deactivated auth/login.js TOKEN_EXPIRED401 Access token has expired middlewares/auth.js ADMIN_ONLY403 Endpoint requires admin role middlewares/auth.js
Orders & Payments
Code Status Description Location PENDING_ORDER_EXISTS409 User has an existing pending order orders/createOrder.js:18 INSUFFICIENT_BALANCE400 Wallet balance too low for payment orders/payOrder.js:35 ORDER_NOT_PENDING400 Order cannot be paid (already completed/cancelled) orders/payOrder.js:22 INSUFFICIENT_STOCK400 Product out of stock orders/createOrder.js:36
Products
Code Status Description PRODUCT_NOT_FOUND404 Product does not exist PRODUCT_INACTIVE400 Product is not available for purchase
Validation Errors
Validation errors use a different format from express-validator:
{
"errors" : [
{
"msg" : "Password must be at least 8 characters" ,
"param" : "password" ,
"location" : "body"
},
{
"msg" : "Passwords do not match" ,
"param" : "passwordConfirm" ,
"location" : "body"
}
]
}
Location: src/middlewares/validation.js
Handling:
if ( error . errors && Array . isArray ( error . errors )) {
error . errors . forEach ( err => {
showFieldError ( err . param , err . msg );
});
}
Common Validation Rules
Registration (validateRegister)
Email must be valid
Username minimum 3 characters
Password minimum 8 characters
Password confirmation must match
Login (validateLogin)
Email must be valid
Password required
Send TRX (validateSendTRX)
Address required
Amount minimum 0.001 TRX
Frontend Error Handling Example
Axios Interceptor
import axios from 'axios' ;
const api = axios . create ({
baseURL: 'http://localhost:3000/api' ,
withCredentials: true
});
// Response interceptor for error handling
api . interceptors . response . use (
( response ) => response ,
async ( error ) => {
const { status , data } = error . response || {};
// Handle specific error codes
switch ( data ?. code ) {
case 'PENDING_ORDER_EXISTS' :
handlePendingOrder ( data . pendingOrderId );
break ;
case 'INSUFFICIENT_BALANCE' :
handleInsufficientBalance ( data );
break ;
case 'TOKEN_EXPIRED' :
await refreshToken ();
return api . request ( error . config ); // Retry request
}
// Handle status codes
switch ( status ) {
case 401 :
handleUnauthorized ();
break ;
case 403 :
handleForbidden ();
break ;
case 500 :
handleServerError ( data . error );
break ;
}
return Promise . reject ( error );
}
);
export default api ;
React Hook for Error Handling
import { useState } from 'react' ;
export function useApiError () {
const [ error , setError ] = useState ( null );
const handleError = ( err ) => {
const errorData = err . response ?. data ;
const status = err . response ?. status ;
// Format error for display
const formattedError = {
message: errorData ?. error || 'An unexpected error occurred' ,
code: errorData ?. code ,
status ,
details: errorData
};
setError ( formattedError );
// Auto-clear after 5 seconds
setTimeout (() => setError ( null ), 5000 );
};
const clearError = () => setError ( null );
return { error , handleError , clearError };
}
Usage:
function CheckoutPage () {
const { error , handleError , clearError } = useApiError ();
const handlePayment = async () => {
try {
await api . post ( `/orders/ ${ orderId } /pay` );
} catch ( err ) {
handleError ( err );
}
};
return (
< div >
{ error && (
< Alert variant = "error" >
{ error . message }
{ error . code === 'INSUFFICIENT_BALANCE' && (
< p > Balance: { error . details . userBalance } TRX </ p >
) }
</ Alert >
) }
< button onClick = { handlePayment } > Pay Now </ button >
</ div >
);
}
Best Practices
Check Error Codes Always check for application-specific error codes before falling back to status codes
User-Friendly Messages Display friendly messages to users, not raw error responses
Log Errors Send 500 errors to monitoring services for investigation
Retry Logic Implement retry logic for transient errors (network, timeouts)
Error Monitoring
Integrate error tracking for production:
import * as Sentry from '@sentry/browser' ;
api . interceptors . response . use (
( response ) => response ,
( error ) => {
// Log 500 errors to Sentry
if ( error . response ?. status >= 500 ) {
Sentry . captureException ( error , {
tags: {
endpoint: error . config . url ,
method: error . config . method
},
extra: {
response: error . response ?. data
}
});
}
return Promise . reject ( error );
}
);
Testing Error Handling
Test error scenarios in your frontend:
import { render , screen } from '@testing-library/react' ;
import { rest } from 'msw' ;
import { setupServer } from 'msw/node' ;
const server = setupServer (
rest . post ( '/api/orders' , ( req , res , ctx ) => {
return res (
ctx . status ( 409 ),
ctx . json ({
error: 'You have a pending order' ,
code: 'PENDING_ORDER_EXISTS' ,
pendingOrderId: '123'
})
);
})
);
beforeAll (() => server . listen ());
afterAll (() => server . close ());
test ( 'displays pending order error' , async () => {
render ( < CheckoutPage /> );
await userEvent . click ( screen . getByText ( 'Create Order' ));
expect ( screen . getByText ( /pending order/ i )). toBeInTheDocument ();
});