Skip to content

[aw] Linter Miner failed #52125

Description

@github-actions

Workflow Failure

Workflow: Linter Miner
Branch: main
Run: https://github.com/github/gh-aw/actions/runs/31518785706

Warning

Engine Failure: The copilot engine terminated unexpectedly.

Last agent output:

{"type":"tool.execution_complete","timestamp":"2026-08-11T17:57:26.675Z","data":{"toolName":"bash","mcpServerName":"","success":true,"result":{"content":"// Package errstringmatch implements a Go analysis linter that flags calls to\n// strings.Contains/HasPrefix/HasSuffix/EqualFold/Index/LastIndex/Compare on\n// err.Error() with a string literal — all perform brittle substring matching on\n// error messages instead of using errors.Is or errors.As.\npackage errstringmatch\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"github.com/github/gh-aw/pkg/linters/internal/analyzerutil\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/astutil\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/filecheck\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/nolint\"\n\t\"golang.org/x/tools/go/analysis\"\n)\n\n// Analyzer is the err-string-match analysis pass.\nvar Analyzer = analyzerutil.New(\"errstringmatch\", \"reports strings.Contains/HasPrefix/HasSuffix/EqualFold/Index/LastIndex/Compare(err.Error(), \\\"...\\\") calls that perform brittle substring matching on error messages\", run)\n\n// brittleErrStringFuncs is the set of strings package functions that perform\n// brittle error-message matching when their first argument is err.Error().\nvar brittleErrStringFuncs = map[string]bool{\n\t\"Contains\":  true,\n\t\"HasPrefix\": true,\n\t\"HasSuffix\": true,\n\t\"EqualFold\": true,\n\t\"Index\":     true,\n\t\"LastIndex\": true,\n\t\"Compare\":   true,\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tnoLintIndex, err := nolint.Index(pass)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgeneratedFiles, err := filecheck.Index(pass)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnodeFilter := []ast.Node{\n\t\t(*ast.CallExpr)(nil),\n\t}\n\n\treturn analyzerutil.Preorder(pass, nodeFilter, func(n ast.Node) {\n\t\touter, ok := n.(*ast.CallExpr)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tposition := pass.Fset.PositionFor(outer.Pos(), false)\n\t\tif filecheck.ShouldSkipFilename(position.Filename, generatedFiles) {\n\t\t\treturn\n\t\t}\n\n\t\t// Match strings.<BrittleFunc>(X, Y)\n\t\tfuncName, matched := brittleErrStringFuncName(pass, outer)\n\t\tif !matched {\n\t\t\treturn\n\t\t}\n\t\tif len(outer.Args) != 2 {\n\t\t\treturn\n\t\t}\n\n\t\t// First arg must be a call to err.Error()\n\t\tif !isErrDotError(pass, outer.Args[0]) {\n\t\t\treturn\n\t\t}\n\n\t\t// Second arg must be a string literal (or at least a string type)\n\t\tif !isStringLiteral(pass, outer.Args[1]) {\n\t\t\treturn\n\t\t}\n\t\tif nolint.HasDirectiveForLinter(position, noLintIndex, \"errstringmatch\") {\n\t\t\treturn\n\t\t}\n\n\t\tpass.ReportRangef(outer, \"avoid strings.%s(err.Error(), ...) — use errors.Is, errors.As, or a sentinel error instead\", funcName)\n\t})\n}\n\n// brittleErrStringFuncName returns the matched strings function name and true\n// when call is a strings.<BrittleFunc>(...) call expression.\nfunc brittleErrStringFuncName(pass *analysis.Pass, call *ast.CallExpr) (string, bool) {\n\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\tif !astutil.IsPkgSelector(pass, sel, \"strings\") {\n\t\treturn \"\", false\n\t}\n\tif brittleErrStringFuncs[sel.Sel.Name] {\n\t\treturn sel.Sel.Name, true\n\t}\n\treturn \"\", false\n}\n\n// isErrDotError returns true when expr is a method call of the form <expr>.Error()\n// where the receiver implements the error interface.\nfunc isErrDotError(pass *analysis.Pass, expr ast.Expr) bool {\n\tcall, ok := expr.(*ast.CallExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tif sel.Sel.Name != \"Error\" {\n\t\treturn false\n\t}\n\tif len(call.Args) != 0 {\n\t\treturn false\n\t}\n\t// Check that the receiver implements the error interface.\n\tt := pass.TypesInfo.TypeOf(sel.X)\n\tif t == nil {\n\t\treturn false\n\t}\n\treturn nolint.ImplementsError(t)\n}\n\n// isStringLiteral returns true when expr is a string literal or untyped string constant.\nfunc isStringLiteral(pass *analysis.Pass, expr ast.Expr) bool {\n\tif astutil.IsStringLiteral(expr) {\n\t\treturn true\n\t}\n\t// Also accept typed/untyped string constants (e.g. a const identifier).\n\tt := pass.TypesInfo.TypeOf(expr)\n\tif t == nil {\n\t\treturn false\n\t}\n\tbasic, ok := t.Underlying().(*types.Basic)\n\treturn ok && basic.Kind() == types.String\n}\n<shellId: 84 completed with exit code 0>","detailedContent":"// Package errstringmatch implements a Go analysis linter that flags calls to\n// strings.Contains/HasPrefix/HasSuffix/EqualFold/Index/LastIndex/Compare on\n// err.Error() with a string literal — all perform brittle substring matching on\n// error messages instead of using errors.Is or errors.As.\npackage errstringmatch\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"github.com/github/gh-aw/pkg/linters/internal/analyzerutil\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/astutil\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/filecheck\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/nolint\"\n\t\"golang.org/x/tools/go/analysis\"\n)\n\n// Analyzer is the err-string-match analysis pass.\nvar Analyzer = analyzerutil.New(\"errstringmatch\", \"reports strings.Contains/HasPrefix/HasSuffix/EqualFold/Index/LastIndex/Compare(err.Error(), \\\"...\\\") calls that perform brittle substring matching on error messages\", run)\n\n// brittleErrStringFuncs is the set of strings package functions that perform\n// brittle error-message matching when their first argument is err.Error().\nvar brittleErrStringFuncs = map[string]bool{\n\t\"Contains\":  true,\n\t\"HasPrefix\": true,\n\t\"HasSuffix\": true,\n\t\"EqualFold\": true,\n\t\"Index\":     true,\n\t\"LastIndex\": true,\n\t\"Compare\":   true,\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tnoLintIndex, err := nolint.Index(pass)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgeneratedFiles, err := filecheck.Index(pass)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnodeFilter := []ast.Node{\n\t\t(*ast.CallExpr)(nil),\n\t}\n\n\treturn analyzerutil.Preorder(pass, nodeFilter, func(n ast.Node) {\n\t\touter, ok := n.(*ast.CallExpr)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tposition := pass.Fset.PositionFor(outer.Pos(), false)\n\t\tif filecheck.ShouldSkipFilename(position.Filename, generatedFiles) {\n\t\t\treturn\n\t\t}\n\n\t\t// Match strings.<BrittleFunc>(X, Y)\n\t\tfuncName, matched := brittleErrStringFuncName(pass, outer)\n\t\tif !matched {\n\t\t\treturn\n\t\t}\n\t\tif len(outer.Args) != 2 {\n\t\t\treturn\n\t\t}\n\n\t\t// First arg must be a call to err.Error()\n\t\tif !isErrDotError(pass, outer.Args[0]) {\n\t\t\treturn\n\t\t}\n\n\t\t// Second arg must be a string literal (or at least a string type)\n\t\tif !isStringLiteral(pass, outer.Args[1]) {\n\t\t\treturn\n\t\t}\n\t\tif nolint.HasDirectiveForLinter(position, noLintIndex, \"errstringmatch\") {\n\t\t\treturn\n\t\t}\n\n\t\tpass.ReportRangef(outer, \"avoid strings.%s(err.Error(), ...) — use errors.Is, errors.As, or a sentinel error instead\", funcName)\n\t})\n}\n\n// brittleErrStringFuncName returns the matched strings function name and true\n// when call is a strings.<BrittleFunc>(...) call expression.\nfunc brittleErrStringFuncName(pass *analysis.Pass, call *ast.CallExpr) (string, bool) {\n\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\tif !astutil.IsPkgSelector(pass, sel, \"strings\") {\n\t\treturn \"\", false\n\t}\n\tif brittleErrStringFuncs[sel.Sel.Name] {\n\t\treturn sel.Sel.Name, true\n\t}\n\treturn \"\", false\n}\n\n// isErrDotError returns true when expr is a method call of the form <expr>.Error()\n// where the receiver implements the error interface.\nfunc isErrDotError(pass *analysis.Pass, expr ast.Expr) bool {\n\tcall, ok := expr.(*ast.CallExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tif sel.Sel.Name != \"Error\" {\n\t\treturn false\n\t}\n\tif len(call.Args) != 0 {\n\t\treturn false\n\t}\n\t// Check that the receiver implements the error interface.\n\tt := pass.TypesInfo.TypeOf(sel.X)\n\tif t == nil {\n\t\treturn false\n\t}\n\treturn nolint.ImplementsError(t)\n}\n\n// isStringLiteral returns true when expr is a string literal or untyped string constant.\nfunc isStringLiteral(pass *analysis.Pass, expr ast.Expr) bool {\n\tif astutil.IsStringLiteral(expr) {\n\t\treturn true\n\t}\n\t// Also accept typed/untyped string constants (e.g. a const identifier).\n\tt := pass.TypesInfo.TypeOf(expr)\n\tif t == nil {\n\t\treturn false\n\t}\n\tbasic, ok := t.Underlying().(*types.Basic)\n\treturn ok && basic.Kind() == types.String\n}\n<shellId: 84 completed with exit code 0>","contents":[{"type":"shell_exit","shellId":"84","exitCode":0,"outputTruncated":false,"cwd":"/home/runner/work/gh-aw/gh-aw","outputPreview":"// Package errstringmatch implements a Go analysis linter that flags calls to\n// strings.Contains/HasPrefix/HasSuffix/EqualFold/Index/LastIndex/Compare on\n// err.Error() with a string literal — all perform brittle substring matching on\n// error messages instead of using errors.Is or errors.As.\npackage errstringmatch\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"github.com/github/gh-aw/pkg/linters/internal/analyzerutil\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/astutil\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/filecheck\"\n\t\"github.com/github/gh-aw/pkg/linters/internal/nolint\"\n\t\"golang.org/x/tools/go/analysis\"\n)\n\n// Analyzer is the err-string-match analysis pass.\nvar Analyzer = analyzerutil.New(\"errstringmatch\", \"reports strings.Contains/HasPrefix/HasSuffix/EqualFold/Index/LastIndex/Compare(err.Error(), \\\"...\\\") calls that perform brittle substring matching on error messages\", run)\n\n// brittleErrStringFuncs is the set of strings package functions that perform\n// brittle error-message matching when their first argument is err.Error().\nvar brittleErrStringFuncs = map[string]bool{\n\t\"Contains\":  true,\n\t\"HasPrefix\": true,\n\t\"HasSuffix\": true,\n\t\"EqualFold\": true,\n\t\"Index\":     true,\n\t\"LastIndex\": true,\n\t\"Compare\":   true,\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tnoLintIndex, err := nolint.Index(pass)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgeneratedFiles, err := filecheck.Index(pass)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnodeFilter := []ast.Node{\n\t\t(*ast.CallExpr)(nil),\n\t}\n\n\treturn analyzerutil.Preorder(pass, nodeFilter, func(n ast.Node) {\n\t\touter, ok := n.(*ast.CallExpr)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tposition := pass.Fset.PositionFor(outer.Pos(), false)\n\t\tif filecheck.ShouldSkipFilename(position.Filename, generatedFiles) {\n\t\t\treturn\n\t\t}\n\n\t\t// Match strings.<BrittleFunc>(X, Y)\n\t\tfuncName, matched := brittleErrStringFuncName(pass, outer)\n\t\tif !matched {\n\t\t\treturn\n\t\t}\n\t\tif len(outer.Args) != 2 {\n\t\t\treturn\n\t\t}\n\n\t\t// First arg must be a call to err.Error()\n\t\tif !isErrDotError(pass, outer.Args[0]) {\n\t\t\treturn\n\t\t}\n\n\t\t// Second arg must be a string literal (or at least a string type)\n\t\tif !isStringLiteral(pass, outer.Args[1]) {\n\t\t\treturn\n\t\t}\n\t\tif nolint.HasDirectiveForLinter(position, noLintIndex, \"errstringmatch\") {\n\t\t\treturn\n\t\t}\n\n\t\tpass.ReportRangef(outer, \"avoid strings.%s(err.Error(), ...) — use errors.Is, errors.As, or a sentinel error instead\", funcName)\n\t})\n}\n\n// brittleErrStringFuncName returns the matched strings function name and true\n// when call is a strings.<BrittleFunc>(...) call expression.\nfunc brittleErrStringFuncName(pass *analysis.Pass, call *ast.CallExpr) (string, bool) {\n\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\tif !astutil.IsPkgSelector(pass, sel, \"strings\") {\n\t\treturn \"\", false\n\t}\n\tif brittleErrStringFuncs[sel.Sel.Name] {\n\t\treturn sel.Sel.Name, true\n\t}\n\treturn \"\", false\n}\n\n// isErrDotError returns true when expr is a method call of the form <expr>.Error()\n// where the receiver implements the error interface.\nfunc isErrDotError(pass *analysis.Pass, expr ast.Expr) bool {\n\tcall, ok := expr.(*ast.CallExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tif sel.Sel.Name != \"Error\" {\n\t\treturn false\n\t}\n\tif len(call.Args) != 0 {\n\t\treturn false\n\t}\n\t// Check that the receiver implements the error interface.\n\tt := pass.TypesInfo.TypeOf(sel.X)\n\tif t == nil {\n\t\treturn false\n\t}\n\treturn nolint.ImplementsError(t)\n}\n\n// isStringLiteral returns true when expr is a string literal or untyped string constant.\nfunc isStringLiteral(pass *analysis.Pass, expr ast.Expr) bool {\n\tif astutil.IsStringLiteral(expr) {\n\t\treturn true\n\t}\n\t// Also accept typed/untyped string constants (e.g. a const identifier).\n\tt := pass.TypesInfo.TypeOf(expr)\n\tif t == nil {\n\t\treturn false\n\t}\n\tbasic, ok := t.Underlying().(*types.Basic)\n\treturn ok && basic.Kind() == types.String\n}\n"}]}}}
{"type":"assistant.message","timestamp":"2026-08-11T17:57:58.605Z","data":{"content":"Now I have enough context. Let's launch the linter-writer sub-agent."}}
{"type":"tool.execution_start","timestamp":"2026-08-11T17:57:58.607Z","data":{"toolName":"task","mcpServerName":""}}
{"type":"subagent.started","timestamp":"2026-08-11T17:57:58.618Z","data":{"agentName":"general-purpose","agentDisplayName":"General Purpose Agent","toolCallId":"toolu_014hFutmZfpieQbdcgXv5zhq"}}
[copilot-sdk-driver] [sdk-driver] error: Execution failed: Error: No model available. Check policy enablement under GitHub Settings > Copilot
{"type":"subagent.completed","timestamp":"2026-08-11T17:57:58.680Z","data":{"agentName":"general-purpose","toolCallId":"toolu_014hFutmZfpieQbdcgXv5zhq"}}
{"type":"tool.execution_complete","timestamp":"2026-08-11T17:57:58.716Z","data":{"toolName":"task","mcpServerName":"","success":false}}
{"type":"subagent.completed","timestamp":"2026-08-11T17:57:58.724Z","data":{"agentName":"research","toolCallId":"toolu_01VPvuS5SFsndrQTMDkmveHA"}}
{"type":"subagent.completed","timestamp":"2026-08-11T17:57:58.725Z","data":{"agentName":"explore","toolCallId":"toolu_01SLWSPamTkXwnN1EYahzKmM"}}
{"type":"subagent.completed","timestamp":"2026-08-11T17:57:58.726Z","data":{"agentName":"general-purpose","toolCallId":"toolu_014hFutmZfpieQbdcgXv5zhq"}}

Action Required

Assign this issue to an agent to debug and fix the issue.

Debug with any coding agent

Use this prompt with any coding agent (GitHub Copilot, Claude, Gemini, etc.):

Debug the agentic workflow failure using https://raw.githubusercontent.com/github/gh-aw/main/debug.md

The failed workflow run is at https://github.com/github/gh-aw/actions/runs/31518785706
Manually invoke the agent

Debug this workflow failure using your favorite Agent CLI and the agentic-workflows prompt.

Tip

Stop reporting this workflow as a failure

To stop a workflow from creating failure issues, set report-failure-as-issue: false in its frontmatter:

safe-outputs:
  report-failure-as-issue: false

Generated from Linter Miner · 191 AIC ·

  • expires on Aug 12, 2026, 6:00 AM UTC

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions