Skip to content

Refactor proxy request error path into shared handler#3323

Merged
lpcox merged 4 commits into
mainfrom
copilot/duplicate-code-error-handling
May 18, 2026
Merged

Refactor proxy request error path into shared handler#3323
lpcox merged 4 commits into
mainfrom
copilot/duplicate-code-error-handling

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 18, 2026

proxyRequest() had three near-identical error blocks for client, upstream response stream, and upstream request failures. This duplicated security-relevant logging/metrics behavior and increased drift risk when evolving the request audit schema.

  • What changed

    • Extracted a shared handleRequestError() helper in containers/api-proxy/proxy-request.js to centralize:
      • duration calculation
      • active_requests decrement + requests_errors_total increment
      • request_error audit logging payload (request_id/provider/method/path/duration/error/upstream_host)
      • response status/body emission
    • Replaced the three inline handlers with one-line calls configured by:
      • statusCode
      • clientMessage
      • optional extraMetrics(duration) callback
  • Preserved behavior

    • Kept per-path response semantics unchanged:
      • client stream error → 400 / "Client error"
      • upstream response stream error → 502 / "Response stream error"
      • upstream proxy request error → 502 / "Proxy error"
    • Preserved proxy-request-specific metrics (requests_total 5xx + request_duration_ms) via extraMetrics.
  • Test coverage update

    • Added focused server.proxy.test.js cases to assert the three error paths still emit the expected HTTP status and JSON payloads after deduplication.
function handleRequestError(err, { statusCode, clientMessage, extraMetrics, ...ctx }) {
  const duration = Date.now() - ctx.startTime;
  metrics.gaugeDec('active_requests', { provider: ctx.provider });
  metrics.increment('requests_errors_total', { provider: ctx.provider });
  if (extraMetrics) extraMetrics(duration);
  logRequest('error', 'request_error', { /* shared audit fields */ });
  if (!ctx.res.headersSent) ctx.res.writeHead(statusCode, { 'Content-Type': 'application/json' });
  ctx.res.end(JSON.stringify({ error: clientMessage, message: err.message }));
}

Copilot AI changed the title [WIP] Refactor duplicate error handling in proxy-request.js Refactor proxy request error path into shared handler May 18, 2026
Copilot finished work on behalf of lpcox May 18, 2026 13:56
Copilot AI requested a review from lpcox May 18, 2026 13:56
@lpcox lpcox marked this pull request as ready for review May 18, 2026 14:49
@lpcox lpcox requested a review from Mossaka as a code owner May 18, 2026 14:49
Copilot AI review requested due to automatic review settings May 18, 2026 14:49
@github-actions

This comment has been minimized.

@github-actions github-actions Bot mentioned this pull request May 18, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors proxyRequest() error handling in the API proxy by extracting duplicated client/upstream error response, metrics, and audit logging logic into a shared helper.

Changes:

  • Added handleRequestError() to centralize error-path metrics, audit logging, and JSON response emission.
  • Replaced three inline error handlers with calls to the shared helper.
  • Added proxy request error-path tests for client stream, upstream response stream, and upstream request failures.
Show a summary per file
File Description
containers/api-proxy/proxy-request.js Introduces shared request error handling and wires it into existing proxy error paths.
containers/api-proxy/server.proxy.test.js Adds focused tests for the refactored proxy error response paths.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 3

Comment on lines +484 to +489
function makeRes() {
return {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(),
end: jest.fn(),
Comment on lines +133 to +140
const duration = Date.now() - startTime;
metrics.gaugeDec('active_requests', { provider });
metrics.increment('requests_errors_total', { provider });
if (extraMetrics) extraMetrics(duration);
logRequest('error', 'request_error', {
request_id: requestId, provider, method: req.method,
path: sanitizeForLog(req.url), duration_ms: duration,
error: sanitizeForLog(err.message), upstream_host: targetHost,
Comment on lines 382 to 392
proxyRes.on('error', (err) => {
const duration = Date.now() - startTime;
metrics.gaugeDec('active_requests', { provider });
metrics.increment('requests_errors_total', { provider });
logRequest('error', 'request_error', {
request_id: requestId, provider, method: req.method,
path: sanitizeForLog(req.url), duration_ms: duration,
error: sanitizeForLog(err.message), upstream_host: targetHost,
handleRequestError(err, {
res,
requestId,
provider,
req,
targetHost,
startTime,
statusCode: 502,
clientMessage: 'Response stream error',
});
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 18, 2026

@copilot fix this failing ci check https://github.com/github/gh-aw-firewall/actions/runs/26037978488/job/76552345291?pr=3323

Fixed in 2d3a161.

I addressed the Integration Tests failure by making the protocol checks less flaky (switched from api.github.com/zen to github.com for success-path curl checks) and increased the one-shot-token cold-build timeout in the first test from 240s to 480s to avoid CI timeout on local image build.

Please re-run the failing check on this latest commit.

Copilot finished work on behalf of lpcox May 18, 2026 15:41
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 18, 2026

@copilot address review feedback

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 18, 2026

@copilot address review feedback

Addressed in fca3bf6.

This updates upstream response-stream error handling to avoid writing JSON after headers are sent (now destroys the response in that case), fixes the test mock to toggle headersSent on writeHead, and expands the proxy error-path tests to assert the centralized metrics summary/logging behavior.

Copilot finished work on behalf of lpcox May 18, 2026 16:00
@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

✅ GitHub API: 2 PR entries confirmed in recent-prs.json
✅ Playwright: GitHub homepage title verified (contains "GitHub")
✅ File verify: Smoke test marker file verified

Result: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

🔥 Smoke Test: Copilot BYOK (Offline) Mode

Test Result
GitHub MCP (list PRs) ✅ PR #3335 "chore: recompile all workflow lock files" retrieved
GitHub.com connectivity ⚠️ Pre-step data unavailable (template not substituted)
File write/read ⚠️ Pre-step data unavailable (template not substituted)
BYOK inference (agent → api-proxy → api.githubcopilot.com) ✅ Responding via BYOK offline mode

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com.

Overall: PASS (core BYOK path confirmed ✅)

cc @Copilot (author), @lpcox @Copilot (assignees)

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

🔬 Smoke Test Results

Test Status
GitHub MCP connectivity
GitHub.com HTTP connectivity
File write/read

PR: Refactor proxy request error path into shared handler
Author: @Copilot · Assignees: @lpcox, @Copilot

Overall: PASS 🟢

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test

PRs: Refactor COPILOT_MODEL resolution to remove duplicated env precedence logic; refactor: extract shared service test imports into service-test-setup.test-utils
✅ GitHub PR review | ❌ Safe Inputs GH CLI | ✅ Playwright | ❌ Tavily | ✅ File write/read | ❌ Discussion query | ✅ Build
Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results\n- GitHub MCP Testing: ❌\n- GitHub.com Connectivity: ❌\n- File Writing Testing: ✅\n- Bash Tool Testing: ✅\n\nOverall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

💎 Faceted by Smoke Gemini

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Note: Java required -Dmaven.repo.local=/tmp/gh-aw/agent/m2repo due to a permission error creating ~/.m2/repository in this environment. All tests passed successfully after using a writable local repo path.

Generated by Build Test Suite for issue #3323 · ● 4.3M ·

@github-actions
Copy link
Copy Markdown
Contributor

Chroot Smoke Test Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.15.0 v20.20.2
Go go1.22.12 go1.22.12

Result: FAIL — Python and Node.js versions differ between host and chroot.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

Check Result
Redis PING ❌ Timeout (no response)
PostgreSQL pg_isready ❌ No response
PostgreSQL SELECT 1 ❌ Skipped (pg_isready failed)

Overall: FAILhost.docker.internal is not reachable from this environment. Service containers appear to be unavailable.

🔌 Service connectivity validated by Smoke Services

@lpcox lpcox merged commit f028e72 into main May 18, 2026
64 of 68 checks passed
@lpcox lpcox deleted the copilot/duplicate-code-error-handling branch May 18, 2026 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Duplicate Code] Near-identical error-handling blocks in proxy-request.js repeated 3 times

3 participants