#Multiple invocation of handler function in useFrontendTool

1 messages · Page 1 of 1 (latest)

slender grove
#

I'm struggling with an issue — my handlers are firing more than once for each call. Even after finishing tool invokation (seems like on other tools calling). I found several issues on github (most are closed), but there are a bit different setups.

Copilotkit v1.10.6 (Direct To LLM)
Model gpt-5-nano (disable parallel calling)

shrewd surgeBOT
#

This solution has been AI generated

It’s usually caused by two things: parallel tool execution and React Strict Mode double‑invoking hooks in development. In v1.10.6, ensure tools aren’t executed in parallel at the adapter level and that each action is registered once per component lifecycle.

Verify adapter config (disable parallel tool calls)

Make sure parallel tool calls are disabled on your LLM adapter (not just the model). For example:

import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/backend";

const runtime = new CopilotRuntime();
const adapter = new OpenAIAdapter({
  // ...other config
  disableParallelToolCalls: true,
});

Avoid duplicate registrations in useFrontendTool/useCopilotAction

CopilotKit dedupes actions by name, but duplicate registrations can still happen if actions are re‑created on each render.

  • Ensure unique action names across your app.
  • Memoize your action definitions so they’re created only once:
const actions = useMemo(() => ([
  {
    name: "exampleAction",
    description: "Example",
    handler: (args) => { /* ... */ },
  },
]), []);

useCopilotAction(actions);

React Strict Mode in development

React Strict Mode intentionally double‑invokes effects/hooks in dev, which can trigger duplicate registrations and handler calls. v1.10.6 includes deduplication that warns: "Found an already registered action with name ${action.name}". Update all relevant packages to exactly v1.10.6 and rely on the built‑in dedupe. Note: This double invocation does not occur in production.

Direct to LLM specifics

Define actions only once per component lifecycle and avoid creating new handler references during re‑renders.

hallow folio
#

Hey @slender grove!

Thanks for raising this - could you help me to understand what you're current setup is like?

slender grove
# hallow folio Hey <@84664832233570304>! Thanks for raising this - could you help me to unders...

@hallow folio pretty simple:

<CopilotKit runtimeUrl="/api/copilotkit">
  <CRMTools /> {/* custom component with useFrontendTools definitions */}
  <CopilotChat />
</CopilotKit>

CRMTools is god component with all 7 tools with similar structure:

  1. @tanstact/react-query mutation, like the next:
  const createLeadMutation = useMutation({
    mutationFn: useConvexMutation(api.leads.create),
  });
  1. useFrontendTool wrapper, like the next:
  useFrontendTool({
    name: "create_or_edit_lead",
    description: "Create a new lead or edit an existing lead",
    parameters: [
      {
        name: "id",
        type: "string",
        description:
          "The id of the lead (undefined if creating a new lead, overvise required!)",
      },
      {
        name: "data",
        type: "string",
        description: dedent`
        JSON encoded lead data with following schema:
            name: v.string(),
            ...
`,
      },
    ],
    render({ args, status, result }) {
       // simple generative UI to show progress of execution and result
    },
    handler: async ({ id, data }) => {
       // calling the mutation here (inside try-catch block and with helpers to structure errors and success in similar way
    },
});
slender grove
#

Any ideas?