Menu
Troubleshooting SuiteCommerce: 15 Common Errors and How to Fix Them
SuiteCommerceSuiteCommerceTroubleshootingErrorsDebuggingDevelopment

Troubleshooting SuiteCommerce: 15 Common Errors and How to Fix Them

February 1, 202610 min read
Back to Blog

Troubleshooting SuiteCommerce: 15 Common Errors and How to Fix Them

You're staring at a cryptic error message. The deployment failed. The client is waiting. Your search results are showing you forum posts from 2019 that don't apply to your version.

We've been there. After years of SuiteCommerce development, we've catalogued the errors that actually happen in production—not the textbook scenarios, but the real problems that eat your hours.

This guide documents 15 common SuiteCommerce errors with their exact symptoms, root causes, and tested solutions. Bookmark it.

Table of Contents


Deployment Errors

Error message and warning display on screen

Error #1: "Deployment Failed: SSP Application Bundle Error"

Symptoms:

  • Gulp deploy completes locally but NetSuite deployment fails
  • Error appears in SuiteCommerce Configuration > SSP Applications
  • Site returns 500 errors after attempted deployment

Error Message:

Error: Failed to deploy SSP Application bundle
Code: SSP_APP_BUNDLE_ERROR
Details: Bundle validation failed - incompatible script version

Root Cause: The SuiteScript version specified in your SSP application doesn't match the scripts in your bundle. This commonly happens when:

  1. Local development environment uses different SuiteScript API version than production
  2. Custom scripts reference API modules not available in your NetSuite account version
  3. Bundle was built against a different SuiteCommerce version

Solution:

Step 1: Verify SuiteScript versions in your entry points

// Check your service files (*.ss files)
// They should specify the correct API version

/**
 * @NApiVersion 2.1
 * @NModuleScope Public
 */
define(['N/record', 'N/search'], function(record, search) {
  // Ensure all N/ modules are available in your NetSuite version
});

Step 2: Check NetSuite account compatibility

NetSuite > Setup > Company > Company Information
Note your "Version" and "Release"

Compare against SuiteCommerce reference documentation for API compatibility

Step 3: Rebuild with correct target

# Clean build artifacts
gulp clean

# Rebuild with explicit version target
gulp deploy --target production --source local

# If using developer tools extension:
suitecommerce-cli deploy --account production

Step 4: Validate bundle before deployment

# Run local validation
gulp validate

# Check for any SuiteScript syntax errors
npm run lint:suitescript

Prevention:

  • Lock your Node.js version in .nvmrc
  • Use package-lock.json to freeze dependencies
  • Test deployments in sandbox before production

Error #2: "INSUFFICIENT_PERMISSION: You do not have permission to access this resource"

Symptoms:

  • Deployment fails at the upload step
  • Web Services access denied errors
  • Sometimes works for one developer but not another

Error Message:

INSUFFICIENT_PERMISSION
You do not have permission to deploy to this website.
User role does not have SuiteCommerce deployment permissions.

Root Cause: The deploying user lacks required permissions. SuiteCommerce deployment requires specific role configurations that are often incomplete.

Solution:

Step 1: Verify role permissions

NetSuite > Setup > Users/Roles > Manage Roles

Required permissions for deployment role:
□ Lists > Documents and Files: Full
□ Setup > SuiteScript: Full
□ Setup > SuiteCommerce Advanced (or SCA): Full
□ Setup > SSP Applications: Full
□ Custom Records > (any custom records used): Full
□ Web Services > SuiteScript: Full

Step 2: Check deployment credentials

// In your deployment configuration (netsuite.json or distro.json)
{
  "credentials": {
    "email": "[email protected]",
    "role": "1001",  // Verify this role ID exists and has permissions
    "account": "1234567"
  }
}

Step 3: Verify token-based authentication (if using)

# Token must be created with a role that has deployment permissions
# Regenerate token if role permissions changed after token creation

NetSuite > Setup > Users/Roles > Access Tokens
Create new token with deployment role
Update local configuration with new token

Step 4: Check two-factor authentication

If 2FA is enforced on the deployment account:
- Use token-based authentication instead of email/password
- Or create a dedicated service account with 2FA exemption

Prevention:

  • Create a dedicated deployment role (clone Administrator, remove unnecessary permissions)
  • Use token-based authentication for CI/CD pipelines
  • Document the required permissions in your team wiki

Error #3: "File Cabinet Upload Failed: Maximum File Size Exceeded"

Symptoms:

  • Deployment fails during file upload phase
  • Works with small changes, fails with large ones
  • Happens more often with image-heavy themes

Error Message:

FILE_UPLOAD_ERROR
Maximum file size exceeded for File Cabinet upload
Limit: 10MB per file, 200MB per deployment bundle

Root Cause: NetSuite File Cabinet has strict size limits. Large images, unminified JavaScript bundles, or source maps can exceed these limits.

Solution:

Step 1: Identify large files

# Find files over 1MB in your deployment
find ./DeployDistribution -type f -size +1M -exec ls -lh {} \;

# Common culprits:
# - Unoptimized product images
# - Source maps in production build
# - Unminified vendor bundles
# - Unused font files

Step 2: Optimize your build

// gulpfile.js - ensure production build excludes source maps
gulp.task('deploy', function() {
  return gulp.src(sources)
    .pipe(gulpif(isProduction, uglify()))
    .pipe(gulpif(isProduction, cleanCSS()))
    .pipe(gulpif(!isProduction, sourcemaps.write('.'))) // Only for dev
    .pipe(gulp.dest(destination));
});

Step 3: Split large bundles

// If using webpack, configure code splitting
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      maxSize: 500000, // 500KB max per chunk
    }
  }
};

Step 4: Remove unused assets

# Find images not referenced in your code
grep -r "\.png\|\.jpg\|\.gif" ./Modules/ > used_images.txt
# Compare against actual images in your assets folder
# Delete unused images

Prevention:

  • Add file size checks to your CI pipeline
  • Use image optimization in your build process
  • Regularly audit and remove unused assets

Theme Compilation Issues

Error #4: "SASS Compilation Error: Undefined Variable"

Symptoms:

  • gulp sass fails with variable not found error
  • Theme looks broken with missing styles
  • Error points to line in _custom-theme.scss

Error Message:

Error: Undefined variable "$primary-color".
       on line 47 of Modules/Theme/sass/_custom-theme.scss

Root Cause: SASS variable import order is wrong, or a required variables file is missing from the compilation path.

Solution:

Step 1: Check variable definition location

// Variables should be in _variables.scss or similar
// Usually at: Modules/[Theme]/Sass/_variables.scss

$primary-color: #3498db !default;
$secondary-color: #2c3e50 !default;

Step 2: Verify import order in entry file

// Modules/[Theme]/Sass/[entry-point].scss

// Variables MUST come first
@import 'variables';        // Define variables
@import 'mixins';           // Mixins can use variables
@import 'base';             // Base styles
@import 'components';       // Components
@import 'custom-theme';     // Custom overrides LAST

Step 3: Check for circular imports

// WRONG: _components.scss importing _variables.scss
// when _variables.scss imports _components.scss

// Use @use and @forward in newer SASS versions
@use 'variables' as vars;

.button {
  background: vars.$primary-color;
}

Step 4: Verify manifest.json sass entry points

{
  "sass": {
    "entry_points": {
      "application": "Modules/Theme/Sass/application.scss"
    },
    "files": [
      "Modules/Theme/Sass/**/*.scss"
    ]
  }
}

Prevention:

  • Establish a clear SASS architecture (7-1 pattern or similar)
  • Use !default flags on all variable definitions
  • Document the import order in your style guide

Error #5: "JavaScript Compilation Error: Unexpected Token"

Symptoms:

  • Gulp compile fails with syntax error
  • Error message points to node_modules or vendor code
  • Works on one developer's machine, fails on another

Error Message:

SyntaxError: Unexpected token '...'
at Module._compile (internal/modules/cjs/loader.js:895:18)
File: node_modules/some-package/dist/index.js

Root Cause: Node.js version mismatch. Modern JavaScript syntax (spread operators, optional chaining) isn't recognized by older Node versions that some developers or CI pipelines still use.

Solution:

Step 1: Check Node.js version

node -v
# SuiteCommerce 2024.x requires Node 18.x or 20.x
# SuiteCommerce 2022.x may work with Node 14.x

Step 2: Lock Node.js version

# Create .nvmrc in project root
echo "18.19.0" > .nvmrc

# Team members run:
nvm use

# Or use volta for automatic version switching

Step 3: Update Babel configuration (if using)

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        node: '18'  // Match your Node version
      }
    }]
  ]
};

Step 4: Transpile problematic dependencies

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        // Include node_modules that need transpiling
        exclude: /node_modules\/(?!(problem-package)\/).*/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
};

Prevention:

  • Require consistent Node.js version across team
  • Use engines field in package.json
  • CI pipeline should use same Node version as development

Error #6: "Template Compilation Error: Unknown Helper"

Symptoms:

  • Handlebars template fails to compile
  • Error mentions undefined helper function
  • Template worked before a SuiteCommerce upgrade

Error Message:

Error: Missing helper: "formatCurrency"
Template: product_details.tpl at line 34

Root Cause: Custom Handlebars helpers weren't registered, or a SuiteCommerce upgrade changed the available helpers.

Solution:

Step 1: Verify helper registration

// Helpers must be registered before templates compile
// Usually in: Modules/[Module]/JavaScript/[Module].js

define([
  'Handlebars'
], function(Handlebars) {
  'use strict';
  
  // Register custom helper
  Handlebars.registerHelper('formatCurrency', function(value, options) {
    const currency = options.hash.currency || 'USD';
    return new Handlebars.SafeString(
      new Intl.NumberFormat('en-US', {
        style: 'currency',
        currency: currency
      }).format(value)
    );
  });
});

Step 2: Check module load order

// distro.json or module manifest
{
  "modules": {
    "CustomHelpers": {
      "path": "Modules/CustomHelpers",
      "loadPriority": 1  // Load early, before templates that use it
    }
  }
}

Step 3: Use built-in helpers when available

{{!-- SuiteCommerce built-in helpers --}}

{{!-- Currency formatting --}}
{{currency price format='$0,0.00'}}

{{!-- Date formatting --}}
{{date orderDate format='MMM DD, YYYY'}}

{{!-- Translation --}}
{{translate 'Add to Cart'}}

{{!-- Conditional --}}
{{#if inStock}}
  <span class="in-stock">{{translate 'In Stock'}}</span>
{{/if}}

Step 4: List available helpers for debugging

// Debug: List all registered helpers
console.log('Registered Handlebars helpers:');
Object.keys(Handlebars.helpers).forEach(function(helper) {
  console.log('  - ' + helper);
});

Prevention:

  • Document all custom helpers
  • Review SuiteCommerce release notes for helper changes
  • Create unit tests for custom helpers

Extension Conflicts

Error #7: "Module Not Found: Cannot Resolve Dependency"

Symptoms:

  • Site loads but specific feature doesn't work
  • Console shows module resolution error
  • Extension worked before installing another extension

Error Message:

Error: Module 'CustomExtension.Model' not found
Cannot resolve dependency at runtime
RequireJS: Script error for: CustomExtension.Model

Root Cause: Module naming collision between extensions, or dependency not properly declared in manifest.

Solution:

Step 1: Check for naming collisions

# Search for duplicate module definitions
grep -r "define('CustomExtension.Model'" ./Modules/
# Should return only ONE result

Step 2: Use unique namespace prefixes

// WRONG: Generic naming
define('ProductConfigurator.View', ...);

// RIGHT: Vendor-prefixed naming
define('Stenbase.ProductConfigurator.View', ...);

Step 3: Verify manifest dependencies

// Module manifest (ns.package.json)
{
  "name": "Stenbase.ProductConfigurator",
  "dependencies": [
    "Product",           // Built-in module
    "Backbone",          // Library
    "Stenbase.Common"    // Your other module
  ]
}

Step 4: Check load order in distro.json

{
  "modules": {
    "Stenbase.Common": {
      "path": "Modules/Stenbase.Common"
    },
    "Stenbase.ProductConfigurator": {
      "path": "Modules/Stenbase.ProductConfigurator",
      "requires": ["Stenbase.Common"]  // Explicit dependency
    }
  }
}

Prevention:

  • Always use vendor prefixes in module names
  • Document inter-module dependencies
  • Test extensions in isolation before combining

Error #8: "Event Handler Conflict: Multiple Handlers Registered"

Symptoms:

  • Button click triggers action multiple times
  • Form submits duplicate data
  • Checkout processes multiple payments

Error Message: Usually no error—just duplicate behavior visible in network tab or console logs.

Root Cause: Multiple extensions registering handlers for the same event without proper cleanup, or event handlers not being unbound when views are destroyed.

Solution:

Step 1: Identify duplicate handlers

// Debug: Check event bindings
var $element = jQuery('[data-action="add-to-cart"]');
var events = jQuery._data($element[0], 'events');
console.log('Click handlers:', events.click.length);
// Should be 1, not 2+

Step 2: Use namespaced events

// Extension A
this.$el.on('click.extensionA', '[data-action="submit"]', this.handleSubmit);

// Extension B
this.$el.on('click.extensionB', '[data-action="submit"]', this.handleOther);

// Clean up properly
this.$el.off('.extensionA');  // Removes only Extension A handlers

Step 3: Use one-time handlers where appropriate

// For events that should only fire once
this.$el.one('click', '[data-action="place-order"]', function() {
  // This handler automatically unbinds after first trigger
  this.placeOrder();
}.bind(this));

Step 4: Implement proper view cleanup

var CustomView = Backbone.View.extend({
  initialize: function() {
    // Store references for cleanup
    this._boundHandler = this.handleEvent.bind(this);
    GlobalEvents.on('cart:updated', this._boundHandler);
  },
  
  destroy: function() {
    // Clean up global event bindings
    GlobalEvents.off('cart:updated', this._boundHandler);
    
    // Clean up DOM event bindings
    this.$el.off();
    
    // Call parent destroy
    Backbone.View.prototype.destroy.apply(this, arguments);
  }
});

Prevention:

  • Always implement destroy/cleanup methods
  • Use namespaced events
  • Audit event bindings during code review

Error #9: "Child View Render Error: Container Not Found"

Symptoms:

  • Extension child view doesn't appear
  • Console error about missing container
  • Parent view renders, child view is empty

Error Message:

Error: Child view container '[data-view="CustomWidget"]' not found in parent template
Cannot render child view: CustomWidget.View

Root Cause: The child view's placeholder element doesn't exist in the parent template, usually due to a template override or version mismatch.

Solution:

Step 1: Verify placeholder exists in template

{{!-- Parent template must have the data-view placeholder --}}
<div class="product-details">
  <h1>{{itemName}}</h1>
  
  {{!-- This placeholder is required for child view --}}
  <div data-view="CustomWidget"></div>
  
  <div data-view="Product.Price"></div>
</div>

Step 2: Check child view registration

// In your view's childViews definition
var ProductDetailsView = BaseView.extend({
  childViews: {
    'CustomWidget': function() {
      return new CustomWidgetView({
        model: this.model
      });
    }
  }
});

Step 3: Use correct extension pattern

// Properly extend existing view's child views
define([
  'ProductDetails.View',
  'CustomWidget.View'
], function(ProductDetailsView, CustomWidgetView) {
  'use strict';
  
  // Store original childViews
  var originalChildViews = ProductDetailsView.prototype.childViews;
  
  // Extend with new child view
  ProductDetailsView.prototype.childViews = _.extend({}, originalChildViews, {
    'CustomWidget': function() {
      return new CustomWidgetView({
        model: this.model
      });
    }
  });
  
  return ProductDetailsView;
});

Step 4: Debug child view rendering

// Add logging to see what's happening
var CustomWidgetView = BaseView.extend({
  template: customWidgetTemplate,
  
  initialize: function() {
    console.log('CustomWidgetView initialized');
    console.log('Container exists:', !!document.querySelector('[data-view="CustomWidget"]'));
  },
  
  render: function() {
    console.log('CustomWidgetView render called');
    return BaseView.prototype.render.apply(this, arguments);
  }
});

Prevention:

  • Test template modifications thoroughly
  • Document required placeholders for extensions
  • Use integration tests that verify child view rendering

NetSuite Sync Failures

Error #10: "Order Sync Failed: Invalid Field Reference"

Symptoms:

  • Orders placed on SuiteCommerce don't appear in NetSuite
  • Sync queue shows failed orders
  • Error mentions invalid field or record reference

Error Message:

INVALID_FLD_VALUE
Invalid field reference 'custbody_custom_field' on record type 'salesorder'
Order ID: WEB-123456 failed to sync

Root Cause: A custom field referenced by SuiteCommerce doesn't exist in NetSuite, was renamed, or has incorrect permissions.

Solution:

Step 1: Verify custom field exists

NetSuite > Customization > Lists, Records, & Fields > Transaction Body Fields
Search for: custbody_custom_field
Confirm it exists and is applied to Sales Order

Step 2: Check field permissions

Custom field > Access tab
Verify the integration role has Edit permission
Check "Store Value" is enabled

Step 3: Verify field ID matches exactly

// In your SuiteCommerce customization or SuiteScript
// Field IDs are case-sensitive in some contexts

// WRONG: Inconsistent casing
order.setValue('custbody_Custom_Field', value);

// RIGHT: Exact match to NetSuite field ID
order.setValue('custbody_custom_field', value);

Step 4: Check for field dependency issues

If custbody_custom_field depends on another field:
- That dependency field must be set first
- Verify the dependency value is valid

NetSuite > Customization > Transaction Body Fields > [Field]
Check "Dependency" and "Filter Using" settings

Step 5: Reprocess failed orders

// SuiteScript to reprocess failed web orders
/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/search', 'N/record', 'N/log'], function(search, record, log) {
  function execute(context) {
    var failedOrders = search.create({
      type: 'salesorder',
      filters: [
        ['custbody_sync_status', 'is', 'Failed'],
        'AND',
        ['mainline', 'is', 'T']
      ]
    });
    
    failedOrders.run().each(function(result) {
      try {
        var order = record.load({
          type: 'salesorder',
          id: result.id
        });
        // Fix and resave
        order.save();
        log.audit('Reprocessed', result.id);
      } catch (e) {
        log.error('Failed to reprocess', result.id + ': ' + e.message);
      }
      return true;
    });
  }
  
  return { execute: execute };
});

Prevention:

  • Test custom field mapping in sandbox first
  • Create field validation in your sync scripts
  • Monitor sync queue with alerts for failures

Error #11: "Inventory Sync Timeout: Search Exceeded Governance"

Symptoms:

  • Inventory levels outdated on SuiteCommerce
  • Scheduled inventory sync fails
  • Products show wrong stock status

Error Message:

SSS_SEARCH_TIMEOUT
Search exceeded maximum execution time
Script: mr_inventory_sync.js
Remaining governance: 0 units

Root Cause: Inventory sync saved search or map/reduce script is processing too many records inefficiently.

Solution:

Step 1: Optimize the saved search

// WRONG: Broad search returning all items
var itemSearch = search.create({
  type: 'item',
  filters: [
    ['isinactive', 'is', 'F']
  ],
  columns: ['itemid', 'quantityavailable', 'custitem_web_available']
});

// RIGHT: Targeted search with date filter
var lastSyncDate = runtime.getCurrentScript().getParameter('custscript_last_sync');

var itemSearch = search.create({
  type: 'item',
  filters: [
    ['isinactive', 'is', 'F'],
    'AND',
    ['modified', 'onorafter', lastSyncDate],  // Only changed items
    'AND',
    ['isonline', 'is', 'T']  // Only web items
  ],
  columns: ['itemid', 'quantityavailable']
});

Step 2: Use Map/Reduce for large datasets

/**
 * @NApiVersion 2.1
 * @NScriptType MapReduceScript
 */
define(['N/search', 'N/record'], function(search, record) {
  
  function getInputData() {
    return search.create({
      type: 'item',
      filters: [/* optimized filters */],
      columns: ['itemid', 'quantityavailable']
    });
  }
  
  function map(context) {
    var item = JSON.parse(context.value);
    // Process single item
    context.write({
      key: item.id,
      value: item.values.quantityavailable
    });
  }
  
  function reduce(context) {
    // Batch updates
    var itemId = context.key;
    var quantity = context.values[0];
    
    record.submitFields({
      type: 'item',
      id: itemId,
      values: {
        'custitem_sc_quantity': quantity
      }
    });
  }
  
  function summarize(summary) {
    log.audit('Inventory sync complete', {
      processed: summary.inputSummary.totalRecords,
      errors: summary.inputSummary.error
    });
  }
  
  return { getInputData, map, reduce, summarize };
});

Step 3: Implement incremental sync

// Only sync items modified since last run
function getModifiedItemsSince(lastSyncTimestamp) {
  return search.create({
    type: 'inventoryitem',
    filters: [
      ['lastmodifieddate', 'onorafter', lastSyncTimestamp],
      'AND',
      ['isonline', 'is', 'T']
    ]
  });
}

// Store last sync timestamp
function updateLastSyncTimestamp() {
  var now = new Date().toISOString();
  // Store in custom record or script parameter
}

Prevention:

  • Implement incremental (delta) sync instead of full sync
  • Monitor governance usage and set alerts
  • Schedule sync during off-peak hours

Payment Gateway Errors

Error #12: "Payment Processing Failed: Gateway Timeout"

Symptoms:

  • Checkout hangs at payment step
  • Order submits but payment status unclear
  • Customer charged but order shows failed

Error Message:

GATEWAY_TIMEOUT
Payment gateway did not respond within timeout period (30000ms)
Transaction ID: TXN-789012
Status: Unknown

Root Cause: Payment gateway response time exceeded SuiteCommerce timeout limits. The payment may have actually succeeded.

Solution:

Step 1: Implement idempotent payment requests

// Generate unique idempotency key BEFORE payment attempt
function generateIdempotencyKey(orderId, attemptNumber) {
  return 'PAY_' + orderId + '_' + attemptNumber + '_' + Date.now();
}

// Use in payment request
var paymentRequest = {
  amount: order.total,
  currency: 'USD',
  idempotency_key: generateIdempotencyKey(order.id, 1),
  // ... other params
};

Step 2: Add retry with exponential backoff

async function processPaymentWithRetry(paymentData, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await paymentGateway.charge(paymentData);
      return result;
    } catch (error) {
      if (error.code === 'TIMEOUT' && attempt < maxRetries) {
        // Exponential backoff: 1s, 2s, 4s
        await sleep(Math.pow(2, attempt - 1) * 1000);
        continue;
      }
      throw error;
    }
  }
}

Step 3: Implement payment status check

// If timeout occurs, check if payment actually succeeded
async function verifyPaymentStatus(transactionId) {
  const status = await paymentGateway.getTransaction(transactionId);
  
  if (status.state === 'COMPLETED') {
    // Payment succeeded despite timeout
    return { success: true, captured: true };
  } else if (status.state === 'PENDING') {
    // Payment in progress
    return { success: false, pending: true };
  } else {
    // Payment failed
    return { success: false, captured: false };
  }
}

Step 4: Increase timeout for slow gateways

// In your payment integration configuration
var gatewayConfig = {
  timeout: 60000,  // 60 seconds instead of default 30
  retries: 2,
  // Ensure your server supports this timeout
};

Prevention:

  • Use idempotency keys for all payment requests
  • Implement comprehensive payment status checking
  • Log all payment attempts for reconciliation
  • Set up alerts for gateway timeout spikes

Error #13: "Payment Failed: Invalid Card Token"

Symptoms:

  • Customer enters card, payment fails immediately
  • Error occurs before gateway is contacted
  • Works for some customers, fails for others

Error Message:

INVALID_TOKEN
Payment token has expired or is invalid
Token: tok_xxxxxx

Root Cause: The payment token (from client-side tokenization) expired before the order was submitted. This happens when customers take too long at checkout or when there's a token/session mismatch.

Solution:

Step 1: Verify token lifecycle

// Tokens typically expire in 10-15 minutes
// Check when token was created vs. when order is submitted

var tokenCreatedAt = paymentForm.getTokenTimestamp();
var now = Date.now();
var tokenAge = (now - tokenCreatedAt) / 1000 / 60; // minutes

if (tokenAge > 10) {
  // Token likely expired, request new one
  paymentForm.reTokenize();
}

Step 2: Implement pre-submit token validation

// Before submitting order, verify token is still valid
async function validateTokenBeforeSubmit(token) {
  try {
    const validation = await paymentGateway.validateToken(token);
    return validation.valid;
  } catch (e) {
    // Token invalid, need new one
    return false;
  }
}

// In checkout flow
var submitOrder = async function() {
  var token = this.model.get('paymentToken');
  
  if (!await validateTokenBeforeSubmit(token)) {
    // Show message and re-collect payment
    this.showMessage('Please re-enter your payment information.');
    this.reloadPaymentForm();
    return;
  }
  
  // Proceed with order
  this.model.submit();
};

Step 3: Handle session timeout gracefully

// Show warning before token expires
var tokenWarningTimer = setTimeout(function() {
  showMessage('Your session is about to expire. Please complete your purchase.');
}, 8 * 60 * 1000); // 8 minutes

var tokenExpireTimer = setTimeout(function() {
  showMessage('Session expired. Please refresh to continue.');
  disableSubmitButton();
}, 12 * 60 * 1000); // 12 minutes

Prevention:

  • Keep checkout flow fast (reduce friction)
  • Show session expiration warnings
  • Implement automatic token refresh
  • Log token age at submission for analytics

Performance Degradation

Developer problem solving and technical support

Error #14: "Page Load Timeout: Resource Loading Failed"

Symptoms:

  • Pages load slowly or partially
  • Browser shows "resource loading failed" errors
  • Performance degrades over time

Error Message (Browser Console):

Failed to load resource: net::ERR_TIMED_OUT
GET https://yourstore.com/asset/large-bundle.js

Root Cause: JavaScript bundles are too large, CDN is misconfigured, or server response times have degraded.

Solution:

Step 1: Analyze bundle size

# Using webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer

# Add to webpack config
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

# Run build and analyze
npm run build

Step 2: Implement code splitting

// Lazy load non-critical modules
define([
  'require'  // RequireJS require for dynamic loading
], function(require) {
  
  // Load Product Configurator only when needed
  function loadConfigurator(callback) {
    require(['ProductConfigurator.View'], function(ConfiguratorView) {
      callback(ConfiguratorView);
    });
  }
  
  // Usage
  $('[data-action="configure"]').on('click', function() {
    loadConfigurator(function(ConfiguratorView) {
      new ConfiguratorView().render();
    });
  });
});

Step 3: Verify CDN configuration

# Check CDN headers
curl -I https://yourstore.com/asset/main.js

# Should see:
# Cache-Control: public, max-age=31536000
# Content-Encoding: gzip (or br for Brotli)
# X-Cache: Hit from CDN

Step 4: Optimize critical rendering path

// Prioritize above-the-fold content
// In your main entry point

// Load critical CSS inline
<style>
  /* Critical CSS for above-fold content */
  .header { /* ... */ }
  .hero { /* ... */ }
</style>

// Defer non-critical CSS
<link rel="preload" href="full-styles.css" as="style" onload="this.rel='stylesheet'">

// Async load non-critical JS
<script src="analytics.js" async></script>

Prevention:

  • Set performance budgets (e.g., main bundle < 500KB)
  • Monitor Core Web Vitals continuously
  • Use lazy loading for below-fold content
  • Regular performance audits

Error #15: "Memory Leak: Page Becomes Unresponsive"

Symptoms:

  • Site slows down the longer customer browses
  • Browser tab eventually crashes
  • Memory usage grows continuously in DevTools

Error Message:

Browser: Page Unresponsive
The page may have become unresponsive due to a running script

Root Cause: JavaScript memory leaks from unremoved event listeners, undisposed Backbone views, or accumulating data in global state.

Solution:

Step 1: Identify leaking views

// Debug: Track view instances
var ViewTracker = {
  views: [],
  
  track: function(view) {
    this.views.push({
      cid: view.cid,
      name: view.constructor.name,
      created: Date.now()
    });
  },
  
  untrack: function(view) {
    this.views = this.views.filter(function(v) {
      return v.cid !== view.cid;
    });
  },
  
  report: function() {
    console.log('Active views:', this.views.length);
    console.table(this.views);
  }
};

// In your base view
var BaseView = Backbone.View.extend({
  initialize: function() {
    ViewTracker.track(this);
  },
  
  destroy: function() {
    ViewTracker.untrack(this);
    Backbone.View.prototype.destroy.apply(this, arguments);
  }
});

Step 2: Fix event listener leaks

// WRONG: Adding listeners without removal
var ProblematicView = Backbone.View.extend({
  initialize: function() {
    $(window).on('resize', this.handleResize.bind(this));
    // Never removed when view is destroyed!
  }
});

// RIGHT: Proper listener management
var CorrectView = Backbone.View.extend({
  initialize: function() {
    this._handleResize = this.handleResize.bind(this);
    $(window).on('resize', this._handleResize);
  },
  
  destroy: function() {
    $(window).off('resize', this._handleResize);
    Backbone.View.prototype.destroy.apply(this, arguments);
  }
});

Step 3: Clear accumulated data

// Clear cached data when navigating away
var ProductCache = {
  _cache: {},
  
  get: function(productId) {
    return this._cache[productId];
  },
  
  set: function(productId, data) {
    // Limit cache size
    var keys = Object.keys(this._cache);
    if (keys.length > 50) {
      delete this._cache[keys[0]];  // Remove oldest
    }
    this._cache[productId] = data;
  },
  
  clear: function() {
    this._cache = {};
  }
};

// Clear on major navigation
Router.on('navigate', function() {
  ProductCache.clear();
});

Step 4: Use Chrome DevTools Memory profiler

1. Open DevTools > Memory tab
2. Take heap snapshot before browsing
3. Browse the site, add items to cart, etc.
4. Take another heap snapshot
5. Compare snapshots to find growing objects
6. Look for "Detached DOM trees" - these are leaks

Prevention:

  • Always implement destroy methods
  • Use weak references where appropriate
  • Limit cache sizes
  • Regular memory profiling in development

Debugging Best Practices

Enable Debug Mode

// Add to URL for verbose logging
https://yourstore.com/?sc_debug=true

// Or set in session
sessionStorage.setItem('sc_debug', 'true');

Use Network Throttling

Test on slow connections to expose race conditions:

  • DevTools > Network > Throttle to "Slow 3G"
  • Many bugs only appear under network latency

Check the Script Log

NetSuite > Customization > Scripting > Script Log
Filter by script type and date
Look for execution errors and governance warnings

Enable SSP Debug Response

// In development, return detailed errors
if (SC.Configuration.isDebugMode) {
  return {
    error: true,
    message: error.message,
    stack: error.stack,
    scriptId: runtime.getCurrentScript().id
  };
}

When to Escalate

Not every problem has a quick fix. Escalate when:

  1. Error persists after documented solutions - You've tried everything here
  2. Involves NetSuite core functionality - Not your customization
  3. Affects payment processing - Financial risk requires expert help
  4. Data integrity is at risk - Orders, customers, inventory
  5. Time-critical - Black Friday is tomorrow

At Stenbase, we offer SuiteCommerce maintenance and support for exactly these situations. Our team has seen (and fixed) these errors hundreds of times.


FAQ

How do I enable SuiteCommerce debug mode?

Add ?sc_debug=true to your URL, or set sessionStorage.setItem('sc_debug', 'true') in browser console. This enables verbose logging.

Where are SuiteScript errors logged?

NetSuite > Customization > Scripting > Script Execution Log. Filter by script, date, and log level to find errors.

My deployment succeeds but the site doesn't update. Why?

CDN caching. Either wait for cache expiration, purge manually through your CDN dashboard, or deploy with cache-busting version numbers.

How do I find which extension is causing conflicts?

Disable extensions one by one and test. Or enable all, then disable half at a time (binary search) to identify the culprit faster.

Payment succeeded but order shows failed. What happened?

Check the payment gateway dashboard for the transaction status. If paid, you likely have a timeout issue—the payment completed but the response didn't reach SuiteCommerce. Manually update the order and implement idempotency keys to prevent this.


Dealing with a SuiteCommerce error not covered here? Contact Stenbase for expert troubleshooting. We've seen it all—and fixed it all.

Need Help with Your NetSuite Project?

Our team of experts is ready to help you achieve your goals.

Related Articles