alibaba/page-agent · error · InvokeError

NO_TOOL_CALL

NO_TOOL_CALL

Error message

No tool call found in response

What it means

After a successful LLM call, OpenAIClient.invoke expects the model to respond with a tool call; if choices[0].message.tool_calls[0].function.name is missing it throws InvokeError NO_TOOL_CALL. This happens when the model answered with plain text (or an empty message) instead of invoking one of the provided tools.

Source

Thrown at packages/llms/src/OpenAIClient.ts:183

					data
				)
			default:
				throw new InvokeError(
					InvokeErrorTypes.INVALID_SCHEMA,
					`Unexpected finish_reason: ${choice.finish_reason}`,
					undefined,
					data
				)
		}

		// Apply normalizeResponse if provided (for fixing format issues automatically)
		const normalizedData = options?.normalizeResponse ? options.normalizeResponse(data) : data
		const normalizedChoice = (normalizedData as any).choices?.[0]

		// Get tool name from response
		const toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name
		if (!toolCallName) {
			throw new InvokeError(
				InvokeErrorTypes.NO_TOOL_CALL,
				'No tool call found in response',
				undefined,
				data
			)
		}

		const tool = tools[toolCallName]
		if (!tool) {
			throw new InvokeError(
				InvokeErrorTypes.UNKNOWN,
				`Tool "${toolCallName}" not found in tools`,
				undefined,
				data
			)
		}

		// Extract and parse tool arguments

View on GitHub (pinned to d02db1ee7c)

Solutions

  1. Check error.data.choices[0].message.content to see what the model actually said
  2. Use a model that supports tool/function calling and ensure tools are passed on the request
  3. Tighten the system prompt to force tool usage; lower temperature
  4. If the provider uses a different response shape, normalize it via the normalizeResponse option

Example fix

// before
const res = await llm.invoke(messages, tools) // model replies with text

// after
// catch and surface the model's textual answer
try {
  const res = await llm.invoke(messages, tools)
} catch (e) {
  if (e.code === 'NO_TOOL_CALL') {
    console.warn('Model said instead:', e.data?.choices?.[0]?.message?.content)
  }
  throw e
}
Defensive patterns

Strategy: fallback

Validate before calling

null

Type guard

function isNoToolCallError(e: unknown): e is InvokeError {
  return e instanceof InvokeError && e.code === InvokeErrorTypes.NO_TOOL_CALL
}

Try / catch

try {
  const res = await llm.invoke(messages, tools)
} catch (e) {
  if (isNoToolCallError(e)) {
    const text = e.data?.choices?.[0]?.message?.content
    return { fallbackAnswer: text } // degrade gracefully to the model's text answer
  }
  throw e
}

Prevention

When it happens

Trigger: invoke() is called with a tools map but the model returns a natural-language completion with no tool_calls; also when a custom normalizeResponse strips or renames the tool_calls field, or the provider doesn't support function calling.

Common situations: Model doesn't support function/tool calling (e.g. some open models behind an OpenAI-compatible facade); prompt leads the model to chat instead of act; temperature too high producing conversational replies; provider returns tool calls under a different field name.

Related errors


AI-assisted analysis of alibaba/page-agent@d02db1ee7c (2026-08-28). Data as JSON: /api/errors/f5a8614762e935dc. Report an issue: GitHub.