assistant-ui icon indicating copy to clipboard operation
assistant-ui copied to clipboard

[BUG] tapLookupResources: Resource not found for lookup: {"key":"__LOCALID_47AWGUp"} using local thread runtime with remote thread list

Open watFiree opened this issue 3 months ago • 2 comments

Hello, After updating assistant-ui to the latest version (0.11) from 0.10, I started encountering an error when initializing a new chat, which crashes the app. On version 0.10 everything works correctly.

Image

I have created a repo which allows to reproduce the error. https://github.com/watFiree/assistant-ui-external-store-example

After a bit of research, I found that the initialize method in RemoteThreadListAdapter causes the error when the response takes too long.

Image

Has anyone encountered this error and found the solution?

watFiree avatar Nov 12 '25 12:11 watFiree

I have this issue too.

kennethaasan avatar Nov 15 '25 04:11 kennethaasan

I worked around it by doing:

"use client";

import { useCallback, useEffect, useMemo, useRef } from "react";
import {
  useChatRuntime,
  AssistantChatTransport,
} from "@assistant-ui/react-ai-sdk";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useAskAI } from "@/components/search/llm/ask-ai-provider";
import { AssistantModal } from "@/components/assistant-ui/assistant-modal";

export function AIAssistant({
  apiUrl,
}: Readonly<{
  apiUrl: string;
}>) {
  const { isOpen, setOpen } = useAskAI();

  const transport = useMemo(
    () =>
      new AssistantChatTransport({
        api: apiUrl,
      }),
    [apiUrl],
  );

  const runtime = useChatRuntime({
    transport,
  });

  const previousIsOpenRef = useRef(isOpen);

  const resetThread = useCallback(() => {
    runtime.thread.reset();
  }, [runtime]);

  useEffect(() => {
    if (previousIsOpenRef.current && !isOpen) {
      // Clearing the thread (instead of switching) avoids the tapLookupResources
      // crash triggered when Assistant UI races on local thread IDs.
      resetThread();
    }

    previousIsOpenRef.current = isOpen;
  }, [isOpen, resetThread]);

  const handleOpenChange = useCallback(
    (nextOpen: boolean) => {
      setOpen(nextOpen);
    },
    [setOpen],
  );

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      <AssistantModal open={isOpen} onOpenChange={handleOpenChange} />
    </AssistantRuntimeProvider>
  );
}

kennethaasan avatar Nov 15 '25 09:11 kennethaasan

I'm not using a modal, so I didn't have that callback. However I was able to catch it like this:

  try {
    threadListItem = useAssistantState(({ threadListItem: x }) => x);
  } catch (error) {
    console.error("Error getting composer thread list item", error);
    transientError = true;
  }

and generally not crash on transientError below. Somehow everything works out (it must reload the component after the transient issue stops happening)

in another spot, I was doing

  const runtime = useRemoteThreadListRuntime({
    // Build the model adapter inside runtimeHook so we can capture the per-thread runtime
    runtimeHook: () => {
      // it actually is in a hook
      // eslint-disable-next-line react-hooks/rules-of-hooks
      const itemRt = useAssistantApi().threadListItem();

      const model: ChatModelAdapter = {
        async run({
          messages,
          abortSignal,
        }: ChatModelRunOptions): Promise<ChatModelRunResult> {
          // use itemRt
        }

I changed that to this and it works - i assume because it's more delayed.

  const runtime = useRemoteThreadListRuntime({
    // Build the model adapter inside runtimeHook so we can capture the per-thread runtime
    runtimeHook: () => {
      // it actually is in a hook
      // eslint-disable-next-line react-hooks/rules-of-hooks
      const assistantApi = useAssistantApi();

      const model: ChatModelAdapter = {
        async run({
          messages,
          abortSignal,
        }: ChatModelRunOptions): Promise<ChatModelRunResult> {
          const itemRt = assistantApi.threadListItem();
          // use itemRT
        }

bleonard avatar Dec 09 '25 01:12 bleonard

According to bleonard, i also find another way to solve it. replace

const remoteId = useAssistantState(({ threadListItem }) => threadListItem.remoteId);

to

const remoteId = useAssistantApi().threadListItem().getState().remoteId;

AllenSHH avatar Dec 11 '25 01:12 AllenSHH