windmill-labs/windmill · error

search requires a non-empty string.

Error message

search requires a non-empty string.

What it means

The diff-search tool searches across workspace diffs for a query string; it requires `args.search` to be a non-empty string and throws this error when it is missing or empty (`(args.search ?? '').length === 0`). Pure argument validation thrown before any diff units are collected.

Source

Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:7105

	}
}

// Literal substring search over the changed lines of every diff in the
// comparison. Materializes all patches first (search cannot skip any), then
// scans in memory — same output conventions as search_app.
async function diffSearch(
	args: {
		against?: 'deployed' | 'parent_workspace'
		search?: string
		file_glob?: string
		max_matches?: number
	},
	ctx: WriteDraftCtx
): Promise<string> {
	const { workspace, toolId, toolCallbacks } = ctx
	const query = args.search ?? ''
	if (query.length === 0) {
		throw new Error('search requires a non-empty string.')
	}
	toolCallbacks.setToolStatus(toolId, { content: `Searching diffs for "${query}"...` })

	const units: DiffSearchUnit[] = []
	const failedPaths: string[] = []
	let unflushedNote = ''
	if (args.against === 'parent_workspace') {
		const parent = forkParentOrThrow(workspace)
		const index = await getForkDiffIndex(workspace, parent, { materializeAll: true })
		if (index.skippedComparison) {
			const message = forkComparisonUnavailableMessage(parent)
			toolCallbacks.setToolStatus(toolId, { content: message })
			return message
		}
		collectDiffSearchUnits(index.entries, units, failedPaths)
	} else {
		const { unflushedPaths } = await flushGlobalDraftSaves(workspace)
		expireWorkspaceDiffList(workspace)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Retry with a non-empty `search` string
  2. To see all changes, use the diff tools per item instead of the search tool

Example fix

// before
searchDiffs({ search: '' })
// after
searchDiffs({ search: 'handle_error' })
Defensive patterns

Strategy: validation

Validate before calling

if (typeof args.search !== 'string' || args.search.length === 0) {
  throw new Error('search requires a non-empty string')
}

Type guard

function hasSearchQuery(args: { search?: string }): args is { search: string } {
  return typeof args.search === 'string' && args.search.length > 0
}

Try / catch

try {
  return await searchDiffs(args)
} catch (e) {
  if (String(e?.message).includes('search requires a non-empty string')) {
    // ask the caller for a query, or fall back to per-item diffs
    return promptForQuery() ?? listAllDiffs()
  }
  throw e
}

Prevention

When it happens

Trigger: Calling the search-diffs tool with `search` omitted, an empty string, or `search: undefined` — e.g. the agent intends a broad scan but sends no query.

Common situations: Template calls with unfilled search placeholders; agents passing null/empty after trimming logic of their own; confusing the search tool with a list-all-diffs capability that needs no query.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/c5b626bcd87b604a. Report an issue: GitHub.