ai
ai copied to clipboard
`TypeError: (intermediate value) is not iterable`
Description
I am using Next.js AI Chatbot template. Every time I try to create a new thread by sending a message caused the above-mentioned error. Now, I narrowed down the error using debugger, but still unable to fix it.
Code example
// code that's throwing the error
const [aiStateDelta, result] = await action(
aiStateSnapshot,
...args
);
File: rsc/shared-client/context.tsx
// rsc/shared-client/context.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
var InternalUIStateProvider = React2.createContext(null);
var InternalAIStateProvider = React2.createContext(void 0);
var InternalActionProvider = React2.createContext(null);
var InternalSyncUIStateProvider = React2.createContext(null);
function InternalAIProvider({
children,
initialUIState,
initialAIState,
initialAIStatePatch,
wrappedActions,
wrappedSyncUIState
}) {
if (!("use" in React2)) {
throw new Error("Unsupported React version.");
}
const uiState = React2.useState(initialUIState);
const setUIState = uiState[1];
const resolvedInitialAIStatePatch = initialAIStatePatch ? React2.use(initialAIStatePatch) : void 0;
initialAIState = React2.useMemo(() => {
if (resolvedInitialAIStatePatch) {
return jsondiffpatch.patch(
jsondiffpatch.clone(initialAIState),
resolvedInitialAIStatePatch
);
}
return initialAIState;
}, [initialAIState, resolvedInitialAIStatePatch]);
const aiState = React2.useState(initialAIState);
const setAIState = aiState[1];
const aiStateRef = React2.useRef(aiState[0]);
React2.useEffect(() => {
aiStateRef.current = aiState[0];
}, [aiState[0]]);
const clientWrappedActions = React2.useMemo(
() => Object.fromEntries(
Object.entries(wrappedActions).map(([key, action]) => [
key,
async (...args) => {
const aiStateSnapshot = aiStateRef.current;
// here
const [aiStateDelta, result] = await action(
aiStateSnapshot,
...args
);
(async () => {
const delta = await aiStateDelta;
if (delta !== void 0) {
aiState[1](
jsondiffpatch.patch(
jsondiffpatch.clone(aiStateSnapshot),
delta
)
);
}
})();
return result;
}
])
),
[wrappedActions]
);
const clientWrappedSyncUIStateAction = React2.useMemo(() => {
if (!wrappedSyncUIState) {
return () => {
};
}
return async () => {
const aiStateSnapshot = aiStateRef.current;
const [aiStateDelta, uiState2] = await wrappedSyncUIState(
aiStateSnapshot
);
if (uiState2 !== void 0) {
setUIState(uiState2);
}
const delta = await aiStateDelta;
if (delta !== void 0) {
const patchedAiState = jsondiffpatch.patch(
jsondiffpatch.clone(aiStateSnapshot),
delta
);
setAIState(patchedAiState);
}
};
}, [wrappedSyncUIState]);
return /* @__PURE__ */ jsx2(InternalAIStateProvider.Provider, { value: aiState, children: /* @__PURE__ */ jsx2(InternalUIStateProvider.Provider, { value: uiState, children: /* @__PURE__ */ jsx2(InternalActionProvider.Provider, { value: clientWrappedActions, children: /* @__PURE__ */ jsx2(
InternalSyncUIStateProvider.Provider,
{
value: clientWrappedSyncUIStateAction,
children
}
) }) }) });
}
My Component Looks like this:
'use client'
import * as React from 'react'
import Textarea from 'react-textarea-autosize'
import { useActions, useUIState } from 'ai/rsc'
import { SpinnerMessage, UserMessage } from './stocks/message'
import { type AI } from '@/lib/chat/actions'
import { Button } from '@/components/ui/button'
import { IconArrowElbow, IconPlus } from '@/components/ui/icons'
import {
Tooltip,
TooltipContent,
TooltipTrigger
} from '@/components/ui/tooltip'
import { useEnterSubmit } from '@/lib/hooks/use-enter-submit'
import { nanoid } from 'nanoid'
import { useRouter } from 'next/navigation'
import { getSession } from 'next-auth/react'
import { useChooseGPT } from '@/lib/hooks/use-choose-gpts'
import { createToast } from 'vercel-toast'
export function PromptForm({
input,
setInput
}: {
input: string
setInput: (value: string) => void
}) {
const router = useRouter()
const { gpt } = useChooseGPT()
const { formRef, onKeyDown } = useEnterSubmit()
const inputRef = React.useRef<HTMLTextAreaElement>(null)
const { submitUserMessage } = useActions()
const [_, setMessages] = useUIState<typeof AI>()
React.useEffect(() => {
if (inputRef.current) {
inputRef.current.focus()
}
}, [])
return (
<form
ref={formRef}
onSubmit={async (e: any) => {
e.preventDefault()
const session = await getSession()
if (!session?.user) {
router.push('/login')
return
}
// Blur focus on mobile
if (window.innerWidth < 600) {
e.target['message']?.blur()
}
const value = input.trim()
if (!value) return
if (!gpt) {
createToast(
'Please select an Agent, Role and Task using the Dropdown Menu',
{ type: 'warning', timeout: 5000, cancel: 'Got It' }
)
return
}
setInput('')
debugger;
// Optimistically add user message UI
setMessages(currentMessages => [
...currentMessages,
{
id: nanoid(),
display: <UserMessage>{value}</UserMessage>
}
])
// also set a loader. remove this from the message state before
// adding the response
//
// WEIRD: if I add the following element and send a message,
// the chat chat does not change to its own URL
//
// adding a timeout to simulate the delay seem to fix the issue
setTimeout(() => {
setMessages(currentMessages => [
...currentMessages,
{ id: nanoid(), display: <SpinnerMessage /> }
])
}, 300)
// Submit and get response message
const responseMessage = await submitUserMessage(value, gpt)
// remove the loader and add the response
setMessages(currentMessages => {
const cloned = [...currentMessages]
const _spinner = cloned.pop()
return [...cloned, responseMessage]
})
}}
>
<div className="relative flex max-h-60 w-full grow flex-col overflow-hidden bg-background px-8 sm:rounded-md sm:border sm:px-12">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="absolute left-0 top-4 size-8 rounded-full bg-background p-0 sm:left-4"
onClick={() => {
router.push('/')
}}
>
<IconPlus />
<span className="sr-only">New Chat</span>
</Button>
</TooltipTrigger>
<TooltipContent>New Chat</TooltipContent>
</Tooltip>
<Textarea
ref={inputRef}
tabIndex={0}
onKeyDown={onKeyDown}
placeholder="Send a message."
className="min-h-[60px] w-full resize-none bg-transparent px-4 py-[1.3rem] focus-within:outline-none sm:text-sm"
autoFocus
spellCheck={false}
autoComplete="off"
autoCorrect="off"
name="message"
rows={1}
value={input}
onChange={e => setInput(e.target.value)}
/>
<div className="absolute right-0 top-4 sm:right-4">
<Tooltip>
<TooltipTrigger asChild>
<Button type="submit" size="icon" disabled={input === ''}>
<IconArrowElbow />
<span className="sr-only">Send message</span>
</Button>
</TooltipTrigger>
<TooltipContent>Send message</TooltipContent>
</Tooltip>
</div>
</div>
</form>
)
}
Application-level Error Message:
Unhandled Runtime Error
TypeError: (intermediate value) is not iterable
Source
components/prompt-form.tsx (116:32) @ async onSubmit
114 |
115 | // Submit and get response message
> 116 | const responseMessage = await submitUserMessage(value, gpt)
| ^
117 |
118 | // remove the loader and add the response
119 | setMessages(currentMessages => {
Additional context
I have been trying to fix this issue since last week. Any help is appreciated. Thank you in advance.
I'm seeing the same error on Vercel, but not on local.
My temp bandaid is to auto retry when that failure happens. The user will only experience a little delay.
// Submit and get response message
let attempts = 0
let responseMessage: any = null
while (attempts < 3) {
try {
responseMessage = await submitUserMessage(value)
if (responseMessage) {
setMessages(currentMessages => [...currentMessages, responseMessage])
}
break // Exit loop on success
} catch (error) {
console.error(`Attempt ${attempts + 1} failed:`, error)
attempts++
if (attempts === 3) {
console.error('Failed to submit user message after 3 attempts.')
}
}
}
were you able to solve this? I'm having the same problem.
This is pretty late but I was having the same issue as I was migrating a project to RSCs and managed to fix it.
The problem for me was the auth check in the middleware, to redirect to the login page if the request came from a user that wasn't logged in, and that was conflicting with the server action request, causing the error.
Fixed it by adding a check to see if the "Accept" field in the header is for a component.
import { auth } from '@/auth'
import { NextResponse } from 'next/server'
// More on how NextAuth.js middleware works: https://authjs.dev/getting-started/migrating-to-v5#details
export default auth(req => {
const url = new URL('/login', req.url)
const acceptHeader = req.headers.get('accept')
// Allow requests for Server Actions and Server Components
if (acceptHeader && acceptHeader.includes('text/x-component')) {
return NextResponse.next()
}
// Redirect to login page if user is not authenticated
if (!req.auth?.user) {
return !req.nextUrl.pathname.includes('/login') // Prevent infinite redirect loop
? NextResponse.redirect(url)
: NextResponse.next()
}
return NextResponse.next()
})
export const config = {
matcher: ['/((?!api|_next/static|favicon|_next/image||.*\\.png$).*)']
}
Any update on this? Today I run my repo in local and encountered this exact issue and couldn't figure out how to debug it. A couple days before it was still fine. On production it's still fine. But for some reason, the issue suddenly appeared. Any help please?
Hey! Did you by chance update all of the dependencies? Can you check what version of next-auth you are using?
I tried to reproduce and got this error after running pnpm up --latest. This command ended up downgrading my next-auth version from 5.0.0-beta.4 to 4.24.7. I ran pnpm install next-auth@beta and it resolved all the issues.
Let me know if that resolves the problem for you.
Please report back if this is still an issue
If you're looking into this issue, you might find this helpful:
https://github.com/vercel/next.js/discussions/64993
It seems like middleware redirects have been disabled for server actions - so if you want to authenticate a server action, you'll need to do that inside the action.
Still getting this error - and no auth middleware like others have mentioned. Works fine on local (altho throws the intermediate value error) but breaks on prod