{"record":{"id":"c5b626bcd87b604a","repo":"windmill-labs/windmill","slug":"search-requires-a-non-empty-string","errorCode":null,"errorMessage":"search requires a non-empty string.","messagePattern":"search requires a non-empty string\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/global/core.ts","lineNumber":7105,"sourceCode":"\t}\n}\n\n// Literal substring search over the changed lines of every diff in the\n// comparison. Materializes all patches first (search cannot skip any), then\n// scans in memory — same output conventions as search_app.\nasync function diffSearch(\n\targs: {\n\t\tagainst?: 'deployed' | 'parent_workspace'\n\t\tsearch?: string\n\t\tfile_glob?: string\n\t\tmax_matches?: number\n\t},\n\tctx: WriteDraftCtx\n): Promise<string> {\n\tconst { workspace, toolId, toolCallbacks } = ctx\n\tconst query = args.search ?? ''\n\tif (query.length === 0) {\n\t\tthrow new Error('search requires a non-empty string.')\n\t}\n\ttoolCallbacks.setToolStatus(toolId, { content: `Searching diffs for \"${query}\"...` })\n\n\tconst units: DiffSearchUnit[] = []\n\tconst failedPaths: string[] = []\n\tlet unflushedNote = ''\n\tif (args.against === 'parent_workspace') {\n\t\tconst parent = forkParentOrThrow(workspace)\n\t\tconst index = await getForkDiffIndex(workspace, parent, { materializeAll: true })\n\t\tif (index.skippedComparison) {\n\t\t\tconst message = forkComparisonUnavailableMessage(parent)\n\t\t\ttoolCallbacks.setToolStatus(toolId, { content: message })\n\t\t\treturn message\n\t\t}\n\t\tcollectDiffSearchUnits(index.entries, units, failedPaths)\n\t} else {\n\t\tconst { unflushedPaths } = await flushGlobalDraftSaves(workspace)\n\t\texpireWorkspaceDiffList(workspace)","sourceCodeStart":7087,"sourceCodeEnd":7123,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/core.ts#L7087-L7123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry with a non-empty `search` string","To see all changes, use the diff tools per item instead of the search tool"],"exampleFix":"// before\nsearchDiffs({ search: '' })\n// after\nsearchDiffs({ search: 'handle_error' })","handlingStrategy":"validation","validationCode":"if (typeof args.search !== 'string' || args.search.length === 0) {\n  throw new Error('search requires a non-empty string')\n}","typeGuard":"function hasSearchQuery(args: { search?: string }): args is { search: string } {\n  return typeof args.search === 'string' && args.search.length > 0\n}","tryCatchPattern":"try {\n  return await searchDiffs(args)\n} catch (e) {\n  if (String(e?.message).includes('search requires a non-empty string')) {\n    // ask the caller for a query, or fall back to per-item diffs\n    return promptForQuery() ?? listAllDiffs()\n  }\n  throw e\n}","preventionTips":["Always supply a concrete query string to the diff-search tool","Trim intended queries but reject empty results instead of sending ''","Use per-item diff tools when you want an overview rather than a text search"],"tags":["arguments","validation","search"],"backgroundTag":"missing-required-argument","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}