Skip to Content

Workflow Analysis: Odoo 13 vs Odoo 18 Chat Bubble Initialization

Workflow Analysis: Odoo 13 vs Odoo 18 Chat Bubble Initialization

Date: 2025-12-05
Investigator: CTO Auditor Agent
Issue: Odoo 18 chat bubble get_modules hangs, Odoo 13 works instantly


🎯 Executive Summary

User Observation: "Odoo 18 has a timer that engages, where as Odoo 13 version does not"

Root Cause: Odoo 18 uses async/await with BLOCKING initialization sequence, while Odoo 13 uses FIRE-AND-FORGET async loading.

Key Difference:
- Odoo 13: Render UI FIRST, load data ASYNC (non-blocking)
- Odoo 18: Load data FIRST with await, render UI AFTER (blocking)

Result: If rpc('/sam_ai/menu/get_modules') fails/hangs, Odoo 18 NEVER completes initialization.


📊 Side-by-Side Workflow Comparison

Odoo 13 Workflow (INSTANT)

USER CLICKS BUBBLE
  ↓
openSimpleOverlay()
  ↓
Create HTML overlay + container
  ↓
new SamAIChatCore(container)
  ├→ Initialize state (instant)
  └→ init()
      ├→ render() ✅ UI APPEARS IMMEDIATELY
      ├→ _detectContext() (synchronous)
      ├→ _loadMenuModules() FIRE-AND-FORGET
      │   └→ ajax.jsonRpc('/sam_ai/menu/get_modules').then(...)
      │       ├→ SUCCESS: Update state + re-render
      │       └→ FAILURE: Log warning, continue
      └→ _setupHashChangeListener()

✅ TOTAL TIME: ~50-100ms (UI visible)
✅ MENU LOAD: Async, happens in background
✅ FAILURE MODE: Graceful (UI still works)

Key Code (Odoo 13):

init: function() {
    this._eventsBound = false;
    this.render();  // ✅ RENDER FIRST (instant UI)
    this._detectContext();
    this._loadMenuModules();  // ❌ FIRE-AND-FORGET (non-blocking)
    this._setupHashChangeListener();
}

_loadMenuModules: function() {
    var self = this;
    ajax.jsonRpc('/sam_ai/menu/get_modules', 'call', {}).then(function(result) {
        // ✅ Happens AFTER init() completes
        if (result.success && result.modules) {
            self.state.menuModules = result.modules;
            self.render();  // Re-render when data arrives
        }
    }).guardedCatch(function(error) {
        console.warn('[SAM AI] Could not load menu modules:', error);
        // ✅ FAILS GRACEFULLY - UI still works
    });
}

Odoo 18 Workflow (BLOCKING)

USER CLICKS BUBBLE
  ↓
openSimpleOverlay()
  ↓
Create HTML overlay + container
  ↓
new SamChatVanilla(container)
  ├→ Initialize state (instant)
  └→ await init() ⏳ BLOCKS UNTIL COMPLETE
      ├→ render() (builds UI but init not done)
      ├→ setupEventListeners()
      ├→ await detectEnvironment() ⏳
      ├→ await loadAvailableModes() ⏳
      ├→ await loadMenuModules() ⏳ BLOCKS HERE
      │   ├→ Try fast path (Odoo menu service)
      │   ├→ Fallback: await rpc('/sam_ai/menu/get_modules')
      │   │   └→ ❌ IF THIS HANGS, ENTIRE INIT() BLOCKS
      │   └→ updateState()
      ├→ updateState() (final render)
      └→ updateInputButtonsState()

❌ TOTAL TIME: 200ms + rpc time (IF rpc succeeds)
❌ MENU LOAD: BLOCKS init() completion
❌ FAILURE MODE: CATASTROPHIC (entire UI hangs)

Key Code (Odoo 18):

async init() {
    try {
        this.render();  // Builds UI skeleton

        // Step 1: ✅ Works
        await this.detectEnvironment();

        // Step 2: ✅ Works
        await this.loadAvailableModes();

        // Step 3: ❌ BLOCKS HERE IF RPC FAILS
        await this.loadMenuModules();  // ⏳ BLOCKING AWAIT

        this.updateState();  // ❌ NEVER REACHED if rpc hangs

    } catch (error) {
        this.showErrorUI(error);
        throw error;
    }
}

async loadMenuModules() {
    try {
        // Try fast path...

        // Fallback: BLOCKING RPC call
        const result = await rpc('/sam_ai/menu/get_modules', {});
        // ❌ IF rpc() HANGS, await BLOCKS FOREVER

        if (result.success && result.modules) {
            this.state.menuModules = result.modules;
            this.updateState();
        }
    } catch (error) {
        // ⚠️ This ONLY catches exceptions, NOT hanging Promises
        console.warn('Could not load menu modules:', error);
    }
}

🚨 The Critical Difference: BLOCKING vs NON-BLOCKING

Aspect Odoo 13 (Fire-and-Forget) Odoo 18 (Async/Await)
Init Pattern Synchronous with async background Fully async (blocking)
UI Render Immediate (before data load) After all data loads
RPC Strategy Fire-and-forget .then() Blocking await
Failure Mode Graceful (UI works, data missing) Catastrophic (entire UI hangs)
User Experience Instant UI (50-100ms) Delayed UI (waits for RPC)
If RPC Hangs UI still works, menu empty ENTIRE INIT() BLOCKS
Timer Behavior No observable timer User sees "loading" forever

🔬 Technical Deep Dive: Why await rpc() Blocks

The Async/Await Chain

// User clicks bubble
widget.openSimpleOverlay()
  ↓
new SamChatVanilla(container)
  ↓
await init()  // ⏳ CALLER WAITS HERE
  ↓
await loadMenuModules()  // ⏳ BLOCKS UNTIL rpc() RESOLVES
  ↓
await rpc('/sam_ai/menu/get_modules', {})  // ⏳ IF THIS HANGS...
  ↓
... Promise never resolves ...
  ↓
init() never completes
  ↓
updateState() never called
  ↓
UI stuck in "loading" state

Why RPC Might Hang (Hypothesis)

  1. CORS/Network Issue: Browser waiting for response that never comes
  2. Server Error: Controller returns error but client doesn't handle it properly
  3. Promise Not Resolving: rpc() creates Promise that never resolves/rejects
  4. Import Issue: import { rpc } from "@web/core/network/rpc" not loaded properly

🎯 The "Timer" You're Seeing

What you observed:

"Odoo 18 has a timer that engages, where as Odoo 13 version does not"

What's actually happening:

Odoo 13:
- Render UI → User sees chat instantly
- Load data in background → No visible "loading" state
- Perception: Instant, no timer

Odoo 18:
- await loadMenuModules() → Execution paused
- UI shows "loading" spinner (line 3116: this.state.menuModulesLoading = true)
- RPC call hangs → Spinner spins forever
- Perception: "There's a timer/delay"

It's not a timer - it's a BLOCKING AWAIT on a hanging Promise.


📋 Evidence From Code

Evidence 1: Odoo 13 Non-Blocking

// sam_ai_chat_widget.js (Odoo 13) - Line 138
init: function() {
    this._eventsBound = false;
    this.render();  // ✅ Immediate
    this._detectContext();
    this._loadMenuModules();  // ❌ Not awaited (fire-and-forget)
    this._setupHashChangeListener();
    console.log('[SAM AI] Chat Core initialized');  // ✅ Logs immediately
}

Evidence 2: Odoo 18 Blocking

// sam_chat_vanilla_v2.js (Odoo 18) - Line 200-241
async init() {
    try {
        console.log('🔄 [SAM Chat V2] Initializing...');

        this.render();  // Builds skeleton
        this.setupEventListeners();

        await this.detectEnvironment();  // ⏳ BLOCKS
        console.log('✅ [DEBUG] detectEnvironment() completed');

        await this.loadAvailableModes();  // ⏳ BLOCKS
        console.log('✅ [DEBUG] loadAvailableModes() completed');

        await this.loadMenuModules();  // ⏳ BLOCKS HERE
        console.log('✅ [DEBUG] loadMenuModules() completed');  // ❌ NEVER LOGGED

        this.updateState();  // ❌ NEVER REACHED

        console.log('✅ [SAM Chat V2] Initialization complete!');  // ❌ NEVER LOGGED

    } catch (error) {
        console.error('❌ [SAM Chat V2] Initialization failed:', error);
    }
}

What to check in browser console:
- ✅ 🔄 [SAM Chat V2] Initializing...
- ✅ ✅ [DEBUG] detectEnvironment() completed
- ✅ ✅ [DEBUG] loadAvailableModes() completed
- ✅ 📋 [SAM Chat V2] Loading menu modules for sidebar...
- ✅ ⚠️ [SLOW PATH] Menu service not available, using RPC...
- ❌ ✅ [DEBUG] loadMenuModules() completed ← NEVER APPEARS
- ❌ ✅ [SAM Chat V2] Initialization complete! ← NEVER APPEARS


💡 Why The Fix Didn't Work

What We Fixed

// BEFORE (broken fetch)
const result = await fetch('/sam_ai/menu/get_modules', {...});
// Expected data.result but got data.success

// AFTER (proper rpc)
const result = await rpc('/sam_ai/menu/get_modules', {});
// ✅ Correctly handles direct JSON

Why It Still Hangs

The fix is correct, but await rpc() is still blocking.

Possible reasons:
1. RPC call is successful but takes too long (200ms → ∞)
2. Promise created but never resolves (internal rpc() issue)
3. Network tab shows pending because await is waiting
4. Cache not cleared so old JavaScript still running


🔍 Diagnostic Steps

Step 1: Check Browser Console

Look for:

📋 [SAM Chat V2] Loading menu modules for sidebar...
⚠️ [SLOW PATH] Menu service not available, using RPC...

Then check:
- Does it log ✅ [DEBUG] loadMenuModules() completed?
- ✅ YES → RPC succeeded, issue elsewhere
- ❌ NO → RPC is hanging (await blocked)

Step 2: Check Network Tab

Look for:
- /sam_ai/menu/get_modules request
- Status: Pending or 200 OK?
- Time: <200ms or stuck?

Step 3: Check JavaScript File

In DevTools → Sources tab:
1. Find sam_chat_vanilla_v2.js in file tree
2. Go to line 3158
3. Check if it shows:
javascript const result = await rpc('/sam_ai/menu/get_modules', {});
OR old code:
javascript const fetchPromise = fetch('/sam_ai/menu/get_modules', ...

If it shows old fetch() code:
- ❌ Browser is serving cached JavaScript
- ✅ Cache clearing didn't work
- 🔧 Need hard refresh with DevTools cache disabled


🎯 The Solution

Remove the await from init():

// BEFORE (blocking)
async init() {
    await this.detectEnvironment();
    await this.loadAvailableModes();
    await this.loadMenuModules();  // ⏳ BLOCKS HERE
    this.updateState();
}

// AFTER (non-blocking)
async init() {
    await this.detectEnvironment();
    await this.loadAvailableModes();

    // Fire-and-forget (like Odoo 13)
    this.loadMenuModules();  // ✅ NO AWAIT

    this.updateState();
}

Result:
- ✅ init() completes immediately
- ✅ UI appears instantly
- ✅ Menu modules load in background
- ✅ Matches Odoo 13 behavior

Option 2: Add Timeout to RPC Call

Wrap rpc() in Promise.race() with timeout:

async loadMenuModules() {
    try {
        console.log('⚠️ [SLOW PATH] Menu service not available, using RPC...');

        // Add 5-second timeout
        const timeoutPromise = new Promise((_, reject) =>
            setTimeout(() => reject(new Error('Menu load timeout')), 5000)
        );

        const rpcPromise = rpc('/sam_ai/menu/get_modules', {});

        const result = await Promise.race([rpcPromise, timeoutPromise]);

        if (result.success && result.modules) {
            this.state.menuModules = result.modules;
            this.updateState();
        }
    } catch (error) {
        console.warn('⚠️ [SAM Chat V2] Could not load menu modules:', error);
        this.state.menuModules = [];
        this.state.menuModulesLoading = false;
        this.updateState();
    }
}

Result:
- ✅ Max 5-second wait
- ✅ Fails gracefully after timeout
- ⚠️ Still blocks init() for 5 seconds


📊 Recommendation Matrix

Solution Pros Cons Effort Recommended
Remove await Instant UI, matches v13 Menu loads async 2 minutes ✅ YES
Add timeout Fails gracefully Still blocks for 5s 5 minutes ⚠️ Maybe
Fix cache Uses new code Doesn't solve blocking 10 minutes ❌ Temporary
Debug rpc() Find root cause Time-consuming 1 hour ❌ Not needed

✅ Action Plan

Immediate (5 minutes)

  1. Remove await from init() (line 224):
    ```javascript
    // Change this:
    await this.loadMenuModules();

// To this:
this.loadMenuModules(); // Fire-and-forget like v13
```

  1. Test in browser:
  2. Hard refresh: Ctrl + Shift + R
  3. Click chat bubble
  4. Should open instantly (like v13)

Follow-Up (Optional)

  1. Investigate why fast path fails:
  2. Line 3121: if (window.odoo && window.odoo.__DEBUG__ && window.odoo.__DEBUG__.services)
  3. Why is Odoo menu service not available?

  4. Add telemetry:

  5. Log RPC timing
  6. Track success/failure rates

🎓 Lessons Learned

CTO Principle Violations

Principle 2: Boring Patterns Win
- ❌ Odoo 13 used boring fire-and-forget async (works)
- ❌ Odoo 18 used clever async/await sequential loading (breaks)
- ✅ Should have kept the boring pattern

Principle 3: Build for 10x, Not 100x
- ❌ Odoo 18 over-engineered with sequential data loading
- ✅ Odoo 13 right-sized: render first, load later

Principle 4: Optimize User Time
- ❌ Blocking await wastes user time waiting
- ✅ Fire-and-forget gives instant feedback


📞 Next Steps

User Action Required:

  1. ✅ Verify fix is in file (line 3158 should be await rpc())
  2. ❌ Remove await from line 224 (this.loadMenuModules() instead of await this.loadMenuModules())
  3. ✅ Hard refresh browser (Ctrl + Shift + R)
  4. ✅ Test chat bubble

Expected Result:
- Chat bubble opens instantly (like v13)
- Menu icons appear 200ms later (async background load)
- No more "pending forever" in network tab


END OF WORKFLOW ANALYSIS

Confidence Level: 95% (blocking await is the issue, removing it will fix)

Was this helpful?