From d1f5634f25510a2f280d6a1120cb867bcc2fa87f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 01:51:06 +0000 Subject: [PATCH 1/3] Initial plan From 696f3686b619e43a2587fcb0657390e575dbf645 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 02:01:23 +0000 Subject: [PATCH 2/3] Add missing secret error detection to handle agent failure Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/handle_agent_failure.cjs | 151 +++++++++++++++++- .../setup/js/handle_agent_failure.test.cjs | 1 + pkg/workflow/notify_comment.go | 10 ++ 3 files changed, 160 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/handle_agent_failure.cjs b/actions/setup/js/handle_agent_failure.cjs index 1c4d246b98..40c4e1961a 100644 --- a/actions/setup/js/handle_agent_failure.cjs +++ b/actions/setup/js/handle_agent_failure.cjs @@ -7,6 +7,7 @@ const { getFooterAgentFailureIssueMessage, getFooterAgentFailureCommentMessage, const { renderTemplate } = require("./messages_core.cjs"); const { getCurrentBranch } = require("./get_current_branch.cjs"); const fs = require("fs"); +const path = require("path"); /** * Attempt to find a pull request for the current branch @@ -165,6 +166,115 @@ gh aw audit } } +/** + * Detect missing secrets or authentication failures from agent logs + * @returns {Promise<{hasMissingSecrets: boolean, secretErrors: string[]}>} + */ +async function detectMissingSecrets() { + try { + // Try to read agent artifacts that might contain log information + const artifactPaths = [ + "/tmp/gh-aw/mcp-logs/", + "/tmp/gh-aw/safe-inputs/logs/", + "/tmp/gh-aw/sandbox/firewall/logs/", + ]; + + const secretErrors = []; + let hasMissingSecrets = false; + + for (const artifactPath of artifactPaths) { + try { + if (!fs.existsSync(artifactPath)) { + continue; + } + + // Check if it's a directory + const stats = fs.statSync(artifactPath); + if (!stats.isDirectory()) { + continue; + } + + // Read all files in the directory + const files = fs.readdirSync(artifactPath); + for (const file of files) { + const filePath = path.join(artifactPath, file); + const fileStats = fs.statSync(filePath); + + if (!fileStats.isFile()) { + continue; + } + + const content = fs.readFileSync(filePath, "utf8"); + + // Check for MCP server failures with authentication/secret issues + // Look for JSON log entries or plain text patterns + if (content.includes('"status":"failed"') || content.includes('"status": "failed"')) { + // Try to parse as JSON to extract server names + try { + // Try parsing as JSON array or NDJSON + const lines = content.split("\n"); + for (const line of lines) { + if (!line.trim() || !line.includes('"status"')) { + continue; + } + + try { + const entry = JSON.parse(line); + if (entry.mcp_servers) { + for (const server of entry.mcp_servers) { + if (server.status === "failed") { + hasMissingSecrets = true; + const errorMsg = `MCP server '${server.name}' failed to start (likely missing credentials or authentication failure)`; + if (!secretErrors.includes(errorMsg)) { + secretErrors.push(errorMsg); + } + } + } + } + } catch (e) { + // Not valid JSON line, continue + } + } + } catch (e) { + // Not JSON format, check for text patterns + } + } + + // Check for common authentication error patterns + const authPatterns = [ + /secret.*not.*found/i, + /authentication.*fail/i, + /missing.*credential/i, + /invalid.*token/i, + /unauthorized.*401/i, + /forbidden.*403/i, + /missing.*api.*key/i, + ]; + + for (const pattern of authPatterns) { + if (pattern.test(content)) { + hasMissingSecrets = true; + // Don't add duplicate generic messages if we already have specific MCP failures + if (secretErrors.length === 0) { + secretErrors.push("Authentication or credential errors detected in logs"); + } + break; + } + } + } + } catch (error) { + // Ignore errors reading individual directories/files + core.debug(`Failed to read ${artifactPath}: ${getErrorMessage(error)}`); + } + } + + return { hasMissingSecrets, secretErrors }; + } catch (error) { + core.warning(`Failed to detect missing secrets: ${getErrorMessage(error)}`); + return { hasMissingSecrets: false, secretErrors: [] }; + } +} + /** * Link an issue as a sub-issue to a parent issue * @param {string} parentNodeId - GraphQL node ID of the parent issue @@ -234,6 +344,16 @@ async function main() { // Try to find a pull request for the current branch const pullRequest = await findPullRequestForCurrentBranch(); + // Detect missing secrets or authentication failures + const { hasMissingSecrets, secretErrors } = await detectMissingSecrets(); + + if (hasMissingSecrets && secretErrors.length > 0) { + core.warning(`Detected ${secretErrors.length} secret/authentication error(s):`); + for (const error of secretErrors) { + core.warning(` - ${error}`); + } + } + // Ensure parent issue exists first let parentIssue; try { @@ -284,7 +404,20 @@ async function main() { }; // Render the comment template - const commentBody = renderTemplate(commentTemplate, templateContext); + let commentBody = renderTemplate(commentTemplate, templateContext); + + // Add missing secret information if detected + if (hasMissingSecrets && secretErrors.length > 0) { + commentBody += "\n\n### ⚠️ Missing Secrets Detected\n\n"; + commentBody += "The following authentication or credential issues were detected:\n\n"; + for (const error of secretErrors) { + commentBody += `- ${error}\n`; + } + commentBody += "\n**Resolution:**\n"; + commentBody += "1. Check your workflow configuration for required secrets\n"; + commentBody += "2. Verify secrets are configured in repository settings\n"; + commentBody += "3. Use `gh aw mcp inspect --check-secrets` to validate\n"; + } // Generate footer for the comment using templated message const ctx = { @@ -327,7 +460,21 @@ async function main() { }; // Render the issue template - const issueBodyContent = renderTemplate(issueTemplate, templateContext); + let issueBodyContent = renderTemplate(issueTemplate, templateContext); + + // Add missing secret information if detected + if (hasMissingSecrets && secretErrors.length > 0) { + issueBodyContent += "\n\n### ⚠️ Missing Secrets Detected\n\n"; + issueBodyContent += "The following authentication or credential issues were detected:\n\n"; + for (const error of secretErrors) { + issueBodyContent += `- ${error}\n`; + } + issueBodyContent += "\n**Resolution Steps:**\n"; + issueBodyContent += "1. Check workflow configuration for required secrets\n"; + issueBodyContent += "2. Verify secrets are configured in repository settings: **Settings** → **Secrets and variables** → **Actions**\n"; + issueBodyContent += "3. Use `gh aw mcp inspect --check-secrets` to validate secret configuration\n"; + issueBodyContent += "4. See [Workflow Health Monitoring Runbook](https://github.com/githubnext/gh-aw/blob/main/.github/aw/runbooks/workflow-health.md) for troubleshooting guidance\n"; + } // Generate footer for the issue using templated message const ctx = { diff --git a/actions/setup/js/handle_agent_failure.test.cjs b/actions/setup/js/handle_agent_failure.test.cjs index b5cdff5375..1326854182 100644 --- a/actions/setup/js/handle_agent_failure.test.cjs +++ b/actions/setup/js/handle_agent_failure.test.cjs @@ -21,6 +21,7 @@ describe("handle_agent_failure.cjs", () => { setFailed: vi.fn(), setOutput: vi.fn(), error: vi.fn(), + debug: vi.fn(), }; global.core = mockCore; diff --git a/pkg/workflow/notify_comment.go b/pkg/workflow/notify_comment.go index 8fa5fca431..cf33c2f6db 100644 --- a/pkg/workflow/notify_comment.go +++ b/pkg/workflow/notify_comment.go @@ -77,6 +77,16 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa // Add artifact download steps once (shared by noop and conclusion steps) steps = append(steps, buildAgentOutputDownloadSteps()...) + // Add agent-artifacts download for handle_agent_failure to detect missing secrets + // This artifact contains MCP logs and other diagnostic information + agentArtifactsDownload := buildArtifactDownloadSteps(ArtifactDownloadConfig{ + ArtifactName: "agent-artifacts", + DownloadPath: "/tmp/gh-aw/", + SetupEnvStep: false, // No environment variable needed + StepName: "Download agent artifacts for diagnostics", + }) + steps = append(steps, agentArtifactsDownload...) + // Add noop processing step if noop is configured if data.SafeOutputs.NoOp != nil { // Build custom environment variables specific to noop From e13b6f50ae1bfb6b5fc6421a850e6a5304009ac2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 02:06:48 +0000 Subject: [PATCH 3/3] Update workflow health runbook with missing secret detection Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/aw/runbooks/workflow-health.md | 13 ++++++++++ .github/workflows/campaign-generator.lock.yml | 6 +++++ actions/setup/js/handle_agent_failure.cjs | 24 +++++-------------- specs/artifacts.md | 7 ++++++ 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.github/aw/runbooks/workflow-health.md b/.github/aw/runbooks/workflow-health.md index e995523f2c..fbc571f6b4 100644 --- a/.github/aw/runbooks/workflow-health.md +++ b/.github/aw/runbooks/workflow-health.md @@ -42,11 +42,24 @@ Use this runbook when: - Safe-inputs action fails - Environment variable not available - Template expression evaluation errors +- MCP server fails to start (status: "failed") **Common Causes**: - Safe-inputs action not configured - Missing required secrets - Incorrect secret references +- Missing API keys or authentication tokens for MCP servers + +**Automatic Detection**: +Starting with recent versions, the handle agent failure feature automatically detects and reports missing secret errors when: +- MCP servers fail to start due to authentication issues +- Workflow logs contain authentication failure patterns +- Required API keys are not configured + +When detected, failure issues will include a "Missing Secrets Detected" section with: +- Specific MCP server failures +- Resolution steps +- Links to troubleshooting documentation ## Investigation Steps diff --git a/.github/workflows/campaign-generator.lock.yml b/.github/workflows/campaign-generator.lock.yml index 92680d68b7..f69b8ca438 100644 --- a/.github/workflows/campaign-generator.lock.yml +++ b/.github/workflows/campaign-generator.lock.yml @@ -1442,6 +1442,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Download agent artifacts for diagnostics + continue-on-error: true + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: agent-artifacts + path: /tmp/gh-aw/ - name: Process No-Op Messages id: noop uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 diff --git a/actions/setup/js/handle_agent_failure.cjs b/actions/setup/js/handle_agent_failure.cjs index 40c4e1961a..221e2d01f6 100644 --- a/actions/setup/js/handle_agent_failure.cjs +++ b/actions/setup/js/handle_agent_failure.cjs @@ -173,11 +173,7 @@ gh aw audit async function detectMissingSecrets() { try { // Try to read agent artifacts that might contain log information - const artifactPaths = [ - "/tmp/gh-aw/mcp-logs/", - "/tmp/gh-aw/safe-inputs/logs/", - "/tmp/gh-aw/sandbox/firewall/logs/", - ]; + const artifactPaths = ["/tmp/gh-aw/mcp-logs/", "/tmp/gh-aw/safe-inputs/logs/", "/tmp/gh-aw/sandbox/firewall/logs/"]; const secretErrors = []; let hasMissingSecrets = false; @@ -199,13 +195,13 @@ async function detectMissingSecrets() { for (const file of files) { const filePath = path.join(artifactPath, file); const fileStats = fs.statSync(filePath); - + if (!fileStats.isFile()) { continue; } const content = fs.readFileSync(filePath, "utf8"); - + // Check for MCP server failures with authentication/secret issues // Look for JSON log entries or plain text patterns if (content.includes('"status":"failed"') || content.includes('"status": "failed"')) { @@ -217,7 +213,7 @@ async function detectMissingSecrets() { if (!line.trim() || !line.includes('"status"')) { continue; } - + try { const entry = JSON.parse(line); if (entry.mcp_servers) { @@ -241,15 +237,7 @@ async function detectMissingSecrets() { } // Check for common authentication error patterns - const authPatterns = [ - /secret.*not.*found/i, - /authentication.*fail/i, - /missing.*credential/i, - /invalid.*token/i, - /unauthorized.*401/i, - /forbidden.*403/i, - /missing.*api.*key/i, - ]; + const authPatterns = [/secret.*not.*found/i, /authentication.*fail/i, /missing.*credential/i, /invalid.*token/i, /unauthorized.*401/i, /forbidden.*403/i, /missing.*api.*key/i]; for (const pattern of authPatterns) { if (pattern.test(content)) { @@ -346,7 +334,7 @@ async function main() { // Detect missing secrets or authentication failures const { hasMissingSecrets, secretErrors } = await detectMissingSecrets(); - + if (hasMissingSecrets && secretErrors.length > 0) { core.warning(`Detected ${secretErrors.length} secret/authentication error(s):`); for (const error of secretErrors) { diff --git a/specs/artifacts.md b/specs/artifacts.md index 70f0b09f1f..a65482d52f 100644 --- a/specs/artifacts.md +++ b/specs/artifacts.md @@ -72,6 +72,9 @@ This section provides an overview of artifacts organized by job name, with dupli **Artifacts Downloaded:** +- `agent-artifacts` + - **Download paths**: `/tmp/gh-aw/` + - **Used in**: 1 workflow(s) - campaign-generator.md - `agent-output` - **Download paths**: `/tmp/gh-aw/safeoutputs/` - **Used in**: 63 workflow(s) - agent-performance-analyzer.md, agent-persona-explorer.md, ai-moderator.md, archie.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, code-scanning-fixer.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, github-mcp-structural-analysis.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-triage-agent.md, layout-spec-maintainer.md, mergefest.md, notion-issue-summary.md, pdf-summary.md, plan.md, poem-bot.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-audit-analyzer.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, security-review.md, slide-deck-maintainer.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md @@ -573,6 +576,10 @@ This section provides an overview of artifacts organized by job name, with dupli - **Download path**: `/tmp/gh-aw/safeoutputs/` - **Depends on jobs**: [activation agent detection safe_outputs] +- **Artifact**: `agent-artifacts` (by name) + - **Download path**: `/tmp/gh-aw/` + - **Depends on jobs**: [activation agent detection safe_outputs] + #### Job: `detection` **Uploads:**