Skip to main content

Overview

The PromoStandards SDK uses Promise-based error handling for all API requests. Understanding how to properly catch and handle errors ensures your application can gracefully handle failures and provide meaningful feedback.

Promise-Based Error Handling

All SDK methods return Promises, which means errors can be handled using standard JavaScript Promise patterns:
import { PromoStandards } from 'promostandards';

const client = new PromoStandards.Client({
  id: 'username',
  password: 'password',
  endpoints: [{
    type: 'ProductData',
    version: '2.0.0',
    url: 'https://api.supplier.com/ProductData'
  }]
});

// Using async/await (recommended)
try {
  const result = await client.productData.getProduct({
    productId: 'ABC123',
    localizationCountry: 'US',
    localizationLanguage: 'en'
  });
  console.log('Success:', result);
} catch (error) {
  console.error('Error:', error.message);
}

// Using .then().catch()
client.productData.getProduct({
  productId: 'ABC123',
  localizationCountry: 'US',
  localizationLanguage: 'en'
})
  .then(result => {
    console.log('Success:', result);
  })
  .catch(error => {
    console.error('Error:', error.message);
  });

How the SDK Handles Errors

From PromoStandards.ts:115-154, the SDK’s request implementation shows how errors are caught:
public promoStandardsAPIRequest(
  serviceAndMethodName: string,
  params: any
): Promise<any> {
  return new Promise((resolve, reject) => {
    const [service, method] = serviceAndMethodName.split('.');
    const endpoint = this.getEndpoint(service as ServiceType);

    // Build SOAP request XML...
    const requestXML: string = soapTemplateIndex[method](
      Object.assign(
        {
          id: this.id,
          password: this.password,
          wsVersion: endpoint.version,
          majorVersion: Utils.majorVersion(endpoint.version),
        },
        params
      )
    );

    // Make HTTP request
    axios
      .post(endpoint.url, requestXML, {
        headers: {
          'Content-Type': 'text/xml',
          SOAPAction: method,
        },
      })
      .then((result: any) => {
        this.format === 'json'
          ? resolve(Utils.convertXMLtoJSON(result.data))
          : resolve(result.data);
      })
      .catch((error: Error) => reject(error));  // Errors are rejected here
  });
}
1

Request Initiated

SDK creates SOAP XML and sends HTTP POST request
2

Error Occurs

Network failure, authentication error, or SOAP fault
3

Promise Rejected

Axios catches the error and the Promise is rejected
4

Your Catch Block

Error propagates to your catch block for handling

Common Error Scenarios

Configuration Errors

Missing Endpoint

When a service endpoint is not configured, getEndpoint() throws a ReferenceError:
try {
  const client = new PromoStandards.Client({
    id: 'username',
    password: 'password',
    endpoints: [
      // ProductData endpoint not configured
    ]
  });

  await client.productData.getProduct(params);
} catch (error) {
  if (error instanceof ReferenceError) {
    console.error('Endpoint not configured:', error.message);
    // Error: 'ProductData' endpoint is undefined
  }
}
From PromoStandards.ts:98-107:
public getEndpoint(serviceName: ServiceType): ServiceEndpointType {
  let endpoint;
  if (this.endpoints && this.endpoints.length > 0) {
    endpoint = this.endpoints.find(
      (x) => x.type === serviceName
    ) as ServiceEndpointType;
    if (endpoint) return endpoint;
  }
  throw new ReferenceError(`'${serviceName}' endpoint is undefined`);
}
Always ensure required service endpoints are configured before making API calls.

Authentication Errors

Invalid credentials typically result in HTTP 401 or 403 errors:
try {
  const result = await client.productData.getProduct(params);
} catch (error) {
  if (error.response) {
    switch (error.response.status) {
      case 401:
        console.error('Unauthorized: Invalid credentials');
        // Update credentials or notify user
        break;
      case 403:
        console.error('Forbidden: Access denied');
        // Check account permissions
        break;
      default:
        console.error('HTTP Error:', error.response.status);
    }
  }
}

Network Errors

Network failures occur when the supplier’s endpoint is unreachable:
try {
  const result = await client.productData.getProduct(params);
} catch (error) {
  if (error.code === 'ECONNREFUSED') {
    console.error('Connection refused: Endpoint may be down');
  } else if (error.code === 'ENOTFOUND') {
    console.error('DNS resolution failed: Check endpoint URL');
  } else if (error.code === 'ETIMEDOUT') {
    console.error('Request timeout: Endpoint not responding');
  } else {
    console.error('Network error:', error.message);
  }
}

SOAP Faults

SOAP services may return faults for business logic errors. These typically come as 200 OK responses with fault information in the XML:
try {
  const result = await client.productData.getProduct({
    productId: 'INVALID-ID',
    localizationCountry: 'US',
    localizationLanguage: 'en'
  });

  // Check for SOAP fault in JSON response
  if (result?.Envelope?.Body?.Fault) {
    const fault = result.Envelope.Body.Fault;
    console.error('SOAP Fault:', fault.faultstring || fault.faultcode);
    // Handle business logic error
  } else {
    // Process successful response
    console.log('Product data:', result);
  }
} catch (error) {
  // Network or other errors
  console.error('Request failed:', error.message);
}
SOAP faults don’t always throw JavaScript errors. Always check the response structure for fault indicators.

XML Parsing Errors

When using format: 'json', XML parsing can fail if the response is malformed:
try {
  const result = await client.productData.getProduct(params);
} catch (error) {
  if (error.message.includes('Non-whitespace before first tag')) {
    console.error('Invalid XML response received');
    // Log raw response for debugging
  } else {
    console.error('Parsing error:', error.message);
  }
}
From Utils.ts:29-50, XML conversion errors are rejected:
export const convertXMLtoJSON = (xml: string): Promise<any> => {
  return new Promise((resolve, reject) => {
    xml2js.parseString(
      xml,
      { /* options */ },
      (err: any, data: any) => {
        if (err) {
          reject(err);  // XML parsing errors rejected here
        }
        resolve(replaceArrayTagsWithArrays(data));
      }
    );
  });
};

Error Handling Best Practices

Never call async SDK methods without error handling:
// ❌ BAD - Unhandled promise rejection
const result = await client.productData.getProduct(params);

// ✅ GOOD - Proper error handling
try {
  const result = await client.productData.getProduct(params);
  // Process result
} catch (error) {
  // Handle error
  console.error('Failed to fetch product:', error.message);
}
Validate response data before accessing nested properties:
try {
  const result = await client.productData.getProduct(params);
  
  // Check for SOAP fault
  if (result?.Envelope?.Body?.Fault) {
    throw new Error(result.Envelope.Body.Fault.faultstring);
  }
  
  // Safe property access with optional chaining
  const product = result?.Envelope?.Body?.getProductResponse?.Product;
  
  if (!product) {
    throw new Error('Product data not found in response');
  }
  
  console.log('Product:', product.productName);
} catch (error) {
  console.error('Error:', error.message);
}
Network errors may be transient. Implement exponential backoff:
async function fetchProductWithRetry(client, params, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await client.productData.getProduct(params);
    } catch (error) {
      if (attempt === maxRetries) {
        throw error; // Final attempt failed
      }
      
      // Only retry on network errors
      if (error.code === 'ETIMEDOUT' || error.code === 'ECONNREFUSED') {
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        console.log(`Retry attempt ${attempt} after ${delay}ms`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error; // Don't retry non-network errors
      }
    }
  }
}

// Usage
try {
  const result = await fetchProductWithRetry(client, params);
  console.log('Success:', result);
} catch (error) {
  console.error('Failed after retries:', error.message);
}
Include context and structured logging:
try {
  const result = await client.productData.getProduct(params);
} catch (error) {
  // Structured error logging
  console.error({
    timestamp: new Date().toISOString(),
    service: 'ProductData',
    method: 'getProduct',
    params: params,
    error: {
      message: error.message,
      code: error.code,
      status: error.response?.status,
      data: error.response?.data
    }
  });
  
  // Re-throw or handle
  throw error;
}
Differentiate between error types:
class PromoStandardsError extends Error {
  constructor(message, code, service, method) {
    super(message);
    this.name = 'PromoStandardsError';
    this.code = code;
    this.service = service;
    this.method = method;
  }
}

class AuthenticationError extends PromoStandardsError {
  constructor(service, method) {
    super('Authentication failed', 'AUTH_ERROR', service, method);
    this.name = 'AuthenticationError';
  }
}

class ConfigurationError extends PromoStandardsError {
  constructor(message) {
    super(message, 'CONFIG_ERROR', null, null);
    this.name = 'ConfigurationError';
  }
}

// Usage
try {
  const result = await client.productData.getProduct(params);
} catch (error) {
  if (error.response?.status === 401) {
    throw new AuthenticationError('ProductData', 'getProduct');
  } else if (error instanceof ReferenceError) {
    throw new ConfigurationError(error.message);
  } else {
    throw new PromoStandardsError(
      error.message,
      'UNKNOWN_ERROR',
      'ProductData',
      'getProduct'
    );
  }
}
Check configuration before making requests:
function validateClient(client, serviceName) {
  if (!client.id || !client.password) {
    throw new Error('Client credentials not configured');
  }
  
  try {
    client.getEndpoint(serviceName);
  } catch (error) {
    throw new Error(`${serviceName} endpoint not configured`);
  }
}

// Usage
try {
  validateClient(client, 'ProductData');
  const result = await client.productData.getProduct(params);
} catch (error) {
  console.error('Validation or request failed:', error.message);
}

Comprehensive Error Handling Example

import { PromoStandards } from 'promostandards';

class PromoStandardsService {
  constructor(config) {
    this.client = new PromoStandards.Client(config);
  }

  async getProduct(productId, country = 'US', language = 'en') {
    try {
      // Validate endpoint configuration
      this.client.getEndpoint('ProductData');

      // Make API request
      const result = await this.client.productData.getProduct({
        productId,
        localizationCountry: country,
        localizationLanguage: language
      });

      // Check for SOAP fault
      if (result?.Envelope?.Body?.Fault) {
        const fault = result.Envelope.Body.Fault;
        throw new Error(`SOAP Fault: ${fault.faultstring || fault.faultcode}`);
      }

      // Extract product data
      const product = result?.Envelope?.Body?.getProductResponse?.Product;
      
      if (!product) {
        throw new Error('Product data not found in response');
      }

      return product;

    } catch (error) {
      // Log error with context
      console.error({
        timestamp: new Date().toISOString(),
        method: 'getProduct',
        productId,
        error: {
          message: error.message,
          type: error.constructor.name,
          code: error.code,
          status: error.response?.status
        }
      });

      // Handle specific error types
      if (error instanceof ReferenceError) {
        throw new Error('ProductData service not configured. Check endpoints.');
      } else if (error.response?.status === 401) {
        throw new Error('Authentication failed. Check credentials.');
      } else if (error.code === 'ETIMEDOUT') {
        throw new Error('Request timeout. Supplier endpoint may be slow.');
      } else if (error.code === 'ECONNREFUSED') {
        throw new Error('Connection refused. Supplier endpoint may be down.');
      } else {
        // Re-throw unexpected errors
        throw error;
      }
    }
  }
}

// Usage
const service = new PromoStandardsService({
  id: process.env.SUPPLIER_USERNAME,
  password: process.env.SUPPLIER_PASSWORD,
  endpoints: [{
    type: 'ProductData',
    version: '2.0.0',
    url: process.env.PRODUCT_DATA_ENDPOINT
  }]
});

try {
  const product = await service.getProduct('ABC123');
  console.log('Product:', product.productName);
} catch (error) {
  console.error('Failed to fetch product:', error.message);
  // Show user-friendly error message
  // Log to error tracking service
  // etc.
}

Debugging Errors

Use XML Format

Switch to format: 'xml' to see raw SOAP responses and identify issues

Check Network Tab

Use browser DevTools or tools like Wireshark to inspect HTTP traffic

Enable Axios Logging

Add Axios interceptors to log all requests and responses

Test Credentials

Create a simple test script to verify authentication works

Axios Request/Response Logging

const axios = require('axios');

// Add request interceptor
axios.interceptors.request.use(
  config => {
    console.log('Request:', {
      method: config.method,
      url: config.url,
      headers: config.headers,
      data: config.data?.substring(0, 500) // Log first 500 chars of XML
    });
    return config;
  },
  error => {
    console.error('Request Error:', error);
    return Promise.reject(error);
  }
);

// Add response interceptor
axios.interceptors.response.use(
  response => {
    console.log('Response:', {
      status: response.status,
      headers: response.headers,
      data: response.data?.substring(0, 500)
    });
    return response;
  },
  error => {
    console.error('Response Error:', {
      status: error.response?.status,
      data: error.response?.data,
      message: error.message
    });
    return Promise.reject(error);
  }
);

Next Steps

Client Configuration

Ensure proper client configuration to avoid errors

Authentication

Learn about authentication best practices

Build docs developers (and LLMs) love