{"record":{"id":"3f89bec02c2602a0","repo":"charmbracelet/crush","slug":"failed-to-search-for-symbol-w","errorCode":null,"errorMessage":"failed to search for symbol: %w","messagePattern":"failed to search for symbol: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/lsp_helpers.go","lineNumber":61,"sourceCode":"\t// All candidates were rejected by the LSP; return the first one\n\t// so the caller gets a meaningful error from their own LSP call.\n\treturn results[0], nil\n}\n\n// resolveSymbolResults greps for a symbol and returns all viable\n// {client, path, position} tuples. Callers that need just one match\n// (definition, rename, call hierarchy) use resolveSymbol; callers that\n// want to iterate all matches (references) use this directly.\nfunc resolveSymbolResults(ctx context.Context, lspManager *lsp.Manager, symbol, workingDir string) ([]*resolvedSymbol, error) {\n\tlspManager.Start(ctx, workingDir)\n\n\t// Use word boundaries to avoid matching inside larger identifiers\n\t// (e.g. \"Bar\" inside \"myBar\"). The symbol is already QuoteMeta'd\n\t// so dots and other regex metacharacters are escaped.\n\tpattern := `\\b` + regexp.QuoteMeta(symbol) + `\\b`\n\tmatches, _, err := searchFiles(ctx, pattern, workingDir, \"\", 100)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to search for symbol: %w\", err)\n\t}\n\tif len(matches) == 0 {\n\t\treturn nil, fmt.Errorf(\"symbol '%s' not found in grep results\", symbol)\n\t}\n\n\tvar results []*resolvedSymbol\n\tfor _, match := range matches {\n\t\tabsPath, err := filepath.Abs(match.path)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tclient := findLSPClient(lspManager, absPath)\n\t\tif client == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tresults = append(results, &resolvedSymbol{","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/lsp_helpers.go#L43-L79","documentation":"resolveSymbolResults falls back to grep (searchFiles) when locating a symbol for LSP operations. If grep itself fails, the error is wrapped as 'failed to search for symbol', hiding the underlying cause (e.g. invalid regex from error 140 or walker failures).","triggerScenarios":"Calling resolveSymbol (used by LSP goto-definition/references/rename helpers) when the underlying grep search errors — most commonly because the symbol string, once embedded in the \\\\b-quoted pattern, is empty or the walker fails.","commonSituations":"Empty symbol parameter producing a degenerate pattern, grep running in a directory that cannot be walked, or cascading from an invalid include pattern.","solutions":["Read the wrapped error to identify the root cause (likely 'invalid regex pattern' or a filesystem error).","Ensure the symbol parameter is a non-empty identifier.","Retry the search; if persistent, verify the working directory is readable.","Check that the symbol doesn't contain characters that break QuoteMeta-embedded patterns (rare, QuoteMeta handles metacharacters)."],"exampleFix":"// before\nresolveSymbol(ctx, \"\", workingDir) // empty symbol\n// after\nresolveSymbol(ctx, \"MyFunction\", workingDir)","handlingStrategy":"try-catch","validationCode":"if symbol == \"\" {\n    return errors.New(\"symbol must be non-empty\")\n}","typeGuard":"null","tryCatchPattern":"resolved, err := resolveSymbol(ctx, sym, dir)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to search\") {\n        // inspect wrapped cause: regex or walker error\n    }\n}","preventionTips":["Pass non-empty identifiers as symbols","Unwrap with errors.Unwrap to see the root grep failure","Ensure the working directory is readable before LSP operations"],"tags":["lsp","grep","search"],"backgroundTag":"symbol-resolution-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}