Skip to content

[BUG] Error: 400 An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. #1153

@szajek

Description

@szajek

System details / 系统信息

Windows, DeepChat v0.5.1

What's wrong? / 出了什么问题?

In v0.4.4 and v0.4.5 this error occurs imadiatelly after using prompt which calls tools from MCP server (STDIO MCP local server).
In v0.4.6 and further this error occurs after exchanging a few messages and calling tools many times. Unfortunatelly it is difficult to replicate.
v0.4.3 and lower works fine.

Image

Example logs from v0.5.1

[2025-12-02 13:53:31.144] [info]  [Agent Loop] Starting agent loop for event: o-IXtn-89zAp3rNMlPTdg with model: deepseek-chat
[2025-12-02 13:53:31.144] [info]  [Agent Loop] Iteration 1 for event: o-IXtn-89zAp3rNMlPTdg
[2025-12-02 13:53:32.260] [error] Agent loop inner error for event o-IXtn-89zAp3rNMlPTdg: Error: 400 An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. (insufficient tool messages following tool_calls message)
    at APIError.generate (file:///C:/Program%20Files/DeepChat/resources/app.asar/node_modules/openai/core/error.mjs:41:20)
    at OpenAI.makeStatusError (file:///C:/Program%20Files/DeepChat/resources/app.asar/node_modules/openai/client.mjs:160:32)
    at OpenAI.makeRequest (file:///C:/Program%20Files/DeepChat/resources/app.asar/node_modules/openai/client.mjs:328:30)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async DeepseekProvider.handleChatCompletion (file:///C:/Program%20Files/DeepChat/resources/app.asar/out/main/index.js:6013:20)
    at async DeepseekProvider.coreStream (file:///C:/Program%20Files/DeepChat/resources/app.asar/out/main/index.js:6337:7)
    at async AgentLoopHandler.startStreamCompletion (file:///C:/Program%20Files/DeepChat/resources/app.asar/out/main/index.js:17862:28)
    at async LLMProviderPresenter.startStreamCompletion (file:///C:/Program%20Files/DeepChat/resources/app.asar/out/main/index.js:18450:5)
    at async StreamGenerationHandler.startStreamCompletion (file:///C:/Program%20Files/DeepChat/resources/app.asar/out/main/index.js:22943:24)
    at async ThreadPresenter.startStreamCompletion (file:///C:/Program%20Files/DeepChat/resources/app.asar/out/main/index.js:25198:5)
    at async Session.<anonymous> (node:electron/js2c/browser_init:2:107253)
[2025-12-02 13:53:32.261] [info]  [Agent Loop] Agent loop completed for event: o-IXtn-89zAp3rNMlPTdg, iterations: 0
[2025-12-02 13:53:32.262] [info]  Agent loop finished for event: o-IXtn-89zAp3rNMlPTdg User stopped: false
[2025-12-02 13:53:32.269] [info]  {
  '2': {
    setBounds: '[function] (e,...t)=>{e={...this.getBounds(),...e},n.call(this,e,...t)}',
    _events: {
      blur: [
        '[function] t=>{e.emit("browser-window-blur",t,this)}',
        '[function] () => {\n' +
          '      console.log(`Window ${windowId} lost focus.`);\n' +
          '      if (this.focusedWindowId === windowId) {\n' +
          '        this.focusedWindowId = null;\n' +
          '      }\n' +
          '      eventBus.sendToMain(WINDOW_EVENTS.WINDOW_BLURRED, windowId);\n' +
          '      if (!shellWindow.isDestroyed()) {\n' +
          '        shellWindow.webContents.send("window-blurred", windowId);\n' +
          '      }\n' +
          '    }'
      ],
      focus: [
        '[function] t=>{e.emit("browser-window-focus",t,this)}',
        '[function] () => {\n' +
          '      console.log(`Window ${windowId} gained focus.`);\n' +
          '      this.focusedWindowId = windowId;\n' +
          '      eventBus.sendToMain(WINDOW_EVENTS.WINDOW_FOCUSED, windowId);\n' +
          '      if (!shellWindow.isDestroyed()) {\n' +
          '        shellWindow.webContents.send("window-focused", windowId);\n' +
          '      }\n' +
          '      this.focusActiveTab(windowId, "focus");\n' +
          '    }'
      ],
      close: [
        '[function] e=>{queueMicrotask((()=>{s||e?.defaultPrevented||(s=setTimeout(emitUnresponsiveEvent,5e3))}))}',
        '[function] function closeHandler() {\n    updateState();\n  }',
        '[function] (event) => {\n' +
          '      console.log(\n' +
          '        `Window ${windowId} close event. isQuitting: ${this.isQuitting}, Platform: ${process.platform}.`\n' +
          '      );\n' +
          '      if (!this.isQuitting) {\n' +
          '        const shouldQuitOnClose = this.configPresenter.getCloseToQuit();\n' +
          '        const shouldPreventDefault = windowId === this.mainWindowId && !shouldQuitOnClose;\n' +
          '        if (shouldPreventDefault) {\n' +
          '          console.log(`Window ${windowId}: Preventing default close behavior, hiding instead.`);\n' +
          '          event.preventDefault();\n' +
          '          if (shellWindow.isFullScreen()) {\n' +
          '            console.log(\n' +
          '              `Window ${windowId} is fullscreen, exiting fullscreen before hiding (close event).`\n' +
          '            );\n' +
          '            shellWindow.once("leave-full-screen", () => {\n' +
          '              console.log(`Window ${windowId} left fullscreen, proceeding with hide (close event).`);\n' +
          '              if (!shellWindow.isDestroyed()) {\n' +
          '                shellWindow.hide();\n' +
          '              } else {\n' +
          '                console.warn(\n' +
          '                  `Window ${windowId} was destroyed after leaving fullscreen, cannot hide (close event).`\n' +
          '                );\n' +
          '              }\n' +
          '            });\n' +
          '            shellWindow.setFullScreen(false);\n' +
          '          } else {\n' +
          '            console.log(`Window ${windowId} is not fullscreen, hiding directly (close event).`);\n' +
          '            shellWindow.hide();\n' +
          '          }\n' +
          '        } else {\n' +
          '          console.log(\n' +
          '            `Window ${windowId}: Allowing default close behavior (app is quitting or macOS last window configured to quit).`\n' +
          '          );\n' +
          '          presenter$1.tabPresenter.closeTabs(windowId);\n' +
          '        }\n' +
          '      } else {\n' +
          '        console.log(`Window ${windowId}: isQuitting is true, allowing default close behavior.`);\n' +
          '      }\n' +
          '    }'
      ],
      show: '[function] ()=>{const e=this.isVisible()&&!this.isMinimized();if(i!==e){i=e;const t=i?"visible":"hidden";this.webContents.emit("-window-visibility-change",t)}}',
      hide: '[function] ()=>{const e=this.isVisible()&&!this.isMinimized();if(i!==e){i=e;const t=i?"visible":"hidden";this.webContents.emit("-window-visibility-change",t)}}',
      minimize: '[function] ()=>{const e=this.isVisible()&&!this.isMinimized();if(i!==e){i=e;const t=i?"visible":"hidden";this.webContents.emit("-window-visibility-change",t)}}',
      maximize: [
        '[function] ()=>{const e=this.isVisible()&&!this.isMinimized();if(i!==e){i=e;const t=i?"visible":"hidden";this.webContents.emit("-window-visibility-change",t)}}',
        '[function] () => {\n' +
          '      console.log(`Window ${windowId} maximized.`);\n' +
          '      if (!shellWindow.isDestroyed()) {\n' +
          '        shellWindow.webContents.send(WINDOW_EVENTS.WINDOW_MAXIMIZED);\n' +
          '        eventBus.sendToMain(WINDOW_EVENTS.WINDOW_MAXIMIZED, windowId);\n' +
          '        this.handleWindowRestore(windowId).catch((error) => {\n' +
          '          console.error(`Error handling restore logic after maximizing window ${windowId}:`, error);\n' +
          '        });\n' +
          '      }\n' +
          '    }'
      ],
      restore: [
        '[function] ()=>{const e=this.isVisible()&&!this.isMinimized();if(i!==e){i=e;const t=i?"visible":"hidden";this.webContents.emit("-window-visibility-change",t)}}',
        '[function] async () => {\n' +
          '      console.log(`Window ${windowId} restored.`);\n' +
          '      this.handleWindowRestore(windowId).catch((error) => {\n' +
          '        console.error(`Error handling restore logic for window ${windowId}:`, error);\n' +
          '      });\n' +
          '      this.focusActiveTab(windowId, "restore");\n' +
          '      shellWindow.webContents.send(WINDOW_EVENTS.WINDOW_UNMAXIMIZED);\n' +
          '      eventBus.sendToMain(WINDOW_EVENTS.WINDOW_RESTORED, windowId);\n' +
          '    }'
      ],
      closed: [
        '[function] ()=>{this._browserViews.forEach((e=>e.webContents?.close({waitForBeforeUnload:!0})))}',
        '[function] function closedHandler() {\n' +
          '    // Unregister listeners and save state\n' +
          '    unmanage();\n' +
          '    saveState();\n' +
          '  }',
        '[function] () => {\n' +
          '      console.log(\n' +
          '        `Window ${windowId} closed event triggered. isQuitting: ${this.isQuitting}, Map size BEFORE delete: ${this.windows.size}`\n' +
          '      );\n' +
          '      const windowIdBeingClosed = windowId;\n' +
          '      shellWindow.removeListener("restore", handleRestore);\n' +
          '      this.windows.delete(windowIdBeingClosed);\n' +
          '      this.windowFocusStates.delete(windowIdBeingClosed);\n' +
          '      shellWindowState.unmanage();\n' +
          '      eventBus.sendToMain(WINDOW_EVENTS.WINDOW_CLOSED, windowIdBeingClosed);\n' +
          '      console.log(\n' +
          '        `Window ${windowIdBeingClosed} closed event handled. Map size AFTER delete: ${this.windows.size}`\n' +
          '      );\n' +
          '      if (this.windows.size === 0 && process.platform !== "darwin") {\n' +
          '        console.log(`Last window closed on non-macOS platform.`);\n' +
          '        if (!this.isQuitting) {\n' +
          '          console.warn(\n' +
          '            `Warning: Last window on non-macOS platform triggered closed event, but app is not marked as quitting. This might indicate window destruction instead of hiding.`\n' +
          '          );\n' +
          '        }\n' +
          '      }\n' +
          '    }'
      ],
      resize: [
        '[function] function stateChangeHandler() {\n' +
          "    // Handles both 'resize' and 'move'\n" +
          '    clearTimeout(stateChangeTimer);\n' +
          '    stateChangeTimer = setTimeout(updateState, eventHandlingDelay);\n' +
          '  }',
        '[function] () => {\n' +
          '      eventBus.sendToMain(WINDOW_EVENTS.WINDOW_RESIZE, windowId);\n' +
          '    }'
      ],
      move: '[function] function stateChangeHandler() {\n' +
        "    // Handles both 'resize' and 'move'\n" +
        '    clearTimeout(stateChangeTimer);\n' +
        '    stateChangeTimer = setTimeout(updateState, eventHandlingDelay);\n' +
        '  }',
      'ready-to-show': '[function] () => {\n' +
        '      console.log(`Window ${windowId} is ready to show.`);\n' +
        '      if (!shellWindow.isDestroyed()) {\n' +
        '        shellWindow.show();\n' +
        '        eventBus.sendToMain(WINDOW_EVENTS.WINDOW_CREATED, windowId);\n' +
        '      } else {\n' +
        '        console.warn(`Window ${windowId} was destroyed before ready-to-show.`);\n' +
        '      }\n' +
        '    }',
      unmaximize: '[function] () => {\n' +
        '      console.log(`Window ${windowId} unmaximized.`);\n' +
        '      if (!shellWindow.isDestroyed()) {\n' +
        '        shellWindow.webContents.send(WINDOW_EVENTS.WINDOW_UNMAXIMIZED);\n' +
        '        eventBus.sendToMain(WINDOW_EVENTS.WINDOW_UNMAXIMIZED, windowId);\n' +
        '        this.handleWindowRestore(windowId).catch((error) => {\n' +
        '          console.error(\n' +
        '            `Error handling restore logic after unmaximizing window ${windowId}:`,\n' +
        '            error\n' +
        '          );\n' +
        '        });\n' +
        '      }\n' +
        '    }',
      'enter-full-screen': '[function] () => {\n' +
        '      console.log(`Window ${windowId} entered fullscreen.`);\n' +
        '      if (!shellWindow.isDestroyed()) {\n' +
        '        shellWindow.webContents.send(WINDOW_EVENTS.WINDOW_ENTER_FULL_SCREEN);\n' +
        '        eventBus.sendToMain(WINDOW_EVENTS.WINDOW_ENTER_FULL_SCREEN, windowId);\n' +
        '        this.handleWindowRestore(windowId).catch((error) => {\n' +
        '          console.error(\n' +
        '            `Error handling restore logic after entering fullscreen for window ${windowId}:`,\n' +
        '            error\n' +
        '          );\n' +
        '        });\n' +
        '      }\n' +
        '    }',
      'leave-full-screen': '[function] () => {\n' +
        '      console.log(`Window ${windowId} left fullscreen.`);\n' +
        '      if (!shellWindow.isDestroyed()) {\n' +
        '        shellWindow.webContents.send(WINDOW_EVENTS.WINDOW_LEAVE_FULL_SCREEN);\n' +
        '        eventBus.sendToMain(WINDOW_EVENTS.WINDOW_LEAVE_FULL_SCREEN, windowId);\n' +
        '        this.handleWindowRestore(windowId).catch((error) => {\n' +
        '          console.error(\n' +
        '            `Error handling restore logic after leaving fullscreen for window ${windowId}:`,\n' +
        '            error\n' +
        '          );\n' +
        '        });\n' +
        '      }\n' +
        '    }'
    },
    _eventsCount: 15,
    _browserViews: [],
    devToolsWebContents: null
  }
}

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions