Skip to content

Race Condition in desktop-js-sdk-sample Due to Missing await on Desktop.config.init() #299

@Joezanini

Description

@Joezanini

Summary

The desktop-js-sdk-sample contains a race condition bug that causes intermittent failures when accessing AgentStateInfo data during widget initialization. The Desktop.config.init() asynchronous function is not being awaited, causing subsequent code to execute before initialization completes.

Severity

Medium-High - Intermittent production failures affecting developers using this sample code as reference

Affected Files

  • desktop-js-sdk-sample/src/sa-ds-sdk.js (Line 294)

Description

In the init() method of the DesktopSDKSample class, Desktop.config.init() is called without the await keyword, despite the method being asynchronous and the containing function being declared as async. Immediately following this call, the code attempts to access properties like Desktop.agentStateInfo.latestData and Desktop.config.clientLocale, which may not be populated yet.

Root Cause

This creates a race condition where:

  • Success case: When network/server response is fast, the Promise resolves before the synchronous code executes, and everything works
  • Failure case: When there's network latency, server delays, or system load, the Promise hasn't resolved yet, resulting in undefined/null values or errors when accessing AgentStateInfo properties

Steps to Reproduce

  1. Deploy widget using the sa-ds-sdk.js sample code
  2. Introduce network latency (throttle to Slow 3G in DevTools)
  3. Initialize the Desktop SDK
  4. Observe intermittent failures accessing Desktop.agentStateInfo.latestData properties

Current Code (Line 292-308)

async init() {
  // Initiating desktop config
  Desktop.config.init();  // ⚠️ Missing await
  // Traverse Shadow DOM and map to corresponding fields:
  this.shadowRoot.querySelector('#userState').innerHTML =
    Desktop.agentStateInfo.latestData.subStatus;
  this.shadowRoot.querySelector('#teamName').innerHTML =
    Desktop.agentStateInfo.latestData.teamName;
  this.shadowRoot.querySelector('#profileId').innerHTML =
    Desktop.agentStateInfo.latestData.agentProfileID;
  // ... more property accesses
}

Expected Code

async init() {
  // Initiating desktop config
  await Desktop.config.init();  // ✅ Properly awaited
  // Traverse Shadow DOM and map to corresponding fields:
  this.shadowRoot.querySelector('#userState').innerHTML =
    Desktop.agentStateInfo.latestData.subStatus;
  this.shadowRoot.querySelector('#teamName').innerHTML =
    Desktop.agentStateInfo.latestData.teamName;
  this.shadowRoot.querySelector('#profileId').innerHTML =
    Desktop.agentStateInfo.latestData.agentProfileID;
  // ... more property accesses
}

Comparison with Other Samples

The headless-crm-widget-sample correctly implements this pattern (Line 31):

async init() {  
  await Desktop.config.init();  // ✅ Correct implementation
  logger.info('Headless Widget Log: init function');
  this.registerEventListeners();
}

Impact

  • Developers: Using this sample code as reference will unknowingly introduce race conditions into production code
  • End Users: Intermittent widget failures, especially in environments with slower network connections or higher latency
  • Support: Increased support tickets for difficult-to-debug intermittent issues

Proposed Fix

Add await keyword on line 294:

await Desktop.config.init();

Additional Recommendations

  1. Review all sample code in the repository for similar async/await issues
  2. Add code comments emphasizing that Desktop.config.init() must be awaited
  3. Consider adding ESLint rules to catch floating Promises in sample code
  4. Update documentation to highlight this requirement

Customer Context

This issue was discovered through a customer support ticket where a developer experienced intermittent loss of AgentStateInfo during initialization. The developer had used the desktop-js-sdk-sample as reference and replicated the pattern without the await keyword.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions