Skip to content

fix: handle non-positive timeout and nil cancel in RequestTimeoutMiddleware#197

Open
Siddh2024 wants to merge 1 commit into
AnkanMisra:mainfrom
Siddh2024:fix/negative-timeout-nil-cancel
Open

fix: handle non-positive timeout and nil cancel in RequestTimeoutMiddleware#197
Siddh2024 wants to merge 1 commit into
AnkanMisra:mainfrom
Siddh2024:fix/negative-timeout-nil-cancel

Conversation

@Siddh2024

@Siddh2024 Siddh2024 commented Jun 5, 2026

Copy link
Copy Markdown

Fixes #196

Bug 1 — Immediate cancellation with timeout <= 0:
context.WithTimeout with a non-positive duration creates an already-expired context, cancelling every request immediately.

Bug 2 — nil cancel causes panic in defer cancel():
When an earlier deadline is shorter than desired, cancel was left nil. The nil guard was fragile.

Fix: Both paths assign cancel = func(){} and the nil guard is removed.

Summary by CodeRabbit

Bug Fixes

  • Improved request timeout middleware reliability to properly handle edge cases and prevent potential issues with timeout cancellation logic.

…leware

- When timeout <= 0, use the existing context directly instead of calling
  context.WithTimeout with a non-positive duration (which cancels immediately).
- When an earlier deadline is kept, assign a no-op cancel func so
  defer cancel() does not panic on a nil function call.

Fixes AnkanMisra#196
@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

@Siddh2024 is attempting to deploy a commit to the ankanmisra's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes RequestTimeoutMiddleware to properly handle non-positive timeout values. The middleware now treats timeout <= 0 as "no timeout" by preserving the original request context and ensuring cancel is always callable via a no-op, enabling unconditional defer cancel().

Changes

Timeout Middleware Fix

Layer / File(s) Summary
Request timeout and deadline handling
gateway/middleware.go
RequestTimeoutMiddleware now explicitly handles non-positive timeouts as no-op by using the original context and a no-op cancel, preserves earlier existing deadlines for positive timeouts, ensures cancel is always non-nil, and unconditionally defers cancel() for safe cleanup.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • AnkanMisra/MicroAI-Paygate#36: Introduced the original RequestTimeoutMiddleware implementation that this PR refines to fix immediate timeout behavior for non-positive values.

Suggested labels

SWoC26, level:intermediate

Suggested reviewers

  • AnkanMisra

Poem

🐰 A timeout that times out right away? Not on my watch!
With zero and negative durations now tamed,
The context flows gently, no phantom deadline blame,
And cancel defers safe—no more nil panics to match.
Clean as a carrot, the fix hops along! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description references the linked issue, explains both bugs clearly, and outlines the fix, but it lacks the required template structure with checkboxes for Type of Change, Affected Areas, Contributor Checklist, Verification section, and Screenshots. Fill in the missing template sections: mark Type of Change (Bug fix and Gateway affected), complete the Contributor Checklist, add verification commands and their results, and remove Screenshots section if not applicable.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main bug fixes in the changeset: handling non-positive timeout values and ensuring safe nil cancel handling in RequestTimeoutMiddleware.
Linked Issues check ✅ Passed The PR properly addresses all acceptance criteria from issue #196: non-positive timeouts preserve the request context, positive timeout behavior remains unchanged, and earlier deadlines are preserved with safe cancel handling.
Out of Scope Changes check ✅ Passed All changes are focused on fixing the two identified bugs in RequestTimeoutMiddleware without introducing unrelated refactors or modifications outside the scope of issue #196.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gateway/middleware.go`:
- Around line 163-167: Add unit tests for RequestTimeoutMiddleware to cover the
two missing branches: (1) timeout <= 0 behavior — create a request with timeout
set to 0 and a handler that intentionally sleeps longer than a positive timeout
would; assert the middleware does NOT cancel the request (handler completes and
response is returned), verifying the middleware uses c.Request.Context() and the
no-op cancel (referencing RequestTimeoutMiddleware and the branch where ctx =
c.Request.Context(); cancel = func() {}); (2) earlier parent-deadline behavior —
create a parent request Context with an earlier deadline than the middleware
timeout and wrap it through RequestTimeoutMiddleware, then assert the request is
canceled based on the parent deadline (handler sees context.Deadline exceeded)
rather than the middleware timeout, verifying middleware respects existing
parent deadlines. Ensure tests live alongside gateway/timeout_test.go and use
the same test utilities for injecting timeouts and inspecting cancellation
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f4d0320-6a1a-4e3c-bcaa-b32481d4d27e

📥 Commits

Reviewing files that changed from the base of the PR and between 8004749 and 3436895.

📒 Files selected for processing (1)
  • gateway/middleware.go

Comment thread gateway/middleware.go
Comment on lines 163 to +167
if timeout <= 0 {
// Preserve the existing behavior for zero/negative values.
ctx, cancel = context.WithTimeout(c.Request.Context(), timeout)
// Zero/negative timeout means no timeout — use the existing context
// without wrapping it, so requests don't cancel immediately.
ctx = c.Request.Context()
cancel = func() {} // no-op to avoid nil cancel panic in defer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify test coverage for zero/negative timeout and earlier deadline cases.

# Search for test functions covering zero, negative, or deadline scenarios
rg -n "func Test.*Timeout" gateway/timeout_test.go

# Look for zero/negative timeout test cases
rg -n -C3 "timeout.*<=.*0|timeout.*0|timeout.*-|negative|zero.*timeout|earlier.*deadline|parent.*deadline" gateway/timeout_test.go

Repository: AnkanMisra/MicroAI-Paygate

Length of output: 276


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the timeout tests file
echo "---- gateway/timeout_test.go ----"
if [ -f gateway/timeout_test.go ]; then
  nl -ba gateway/timeout_test.go | sed -n '1,220p'
else
  echo "Missing gateway/timeout_test.go"
fi

# Search across gateway tests for zero/negative timeout and parent-deadline behavior
echo "---- rg across gateway for zero/negative timeout / parent deadline ----"
rg -n --hidden --glob 'gateway/**/*_test.go' \
  "timeout.*(<|<=).?0|<=\s*0|<\s*0|timeout.*0|zero.*timeout|negative|earlier.*deadline|parent.*deadline|context.*deadline.*(exceed|before|earlier)" \
  gateway || true

Repository: AnkanMisra/MicroAI-Paygate

Length of output: 165


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- gateway/timeout_test.go (first 220 lines) ----"
if [ -f gateway/timeout_test.go ]; then
  cat -n gateway/timeout_test.go | sed -n '1,220p'
else
  echo "Missing gateway/timeout_test.go"
fi

echo "---- rg across gateway tests for zero/negative timeout / parent deadline ----"
rg -n --glob 'gateway/**/*_test.go' \
  "(timeout.*(<|<=).?0|<=\s*0|<\s*0|negative|zero.*timeout|earlier.*deadline|parent.*deadline|context.*deadline.*(exceed|before|earlier))" \
  gateway || true

Repository: AnkanMisra/MicroAI-Paygate

Length of output: 5729


Add missing tests for RequestTimeoutMiddleware timeout<=0 and earlier parent-deadline behavior

  • gateway/timeout_test.go only tests the positive-timeout path (504) and AI timeout (~1s) plus provider context deadline; it has no coverage for the timeout <= 0 branch (zero/negative) or for “earlier parent deadline” vs middleware timeout behavior.
  • Existing “negative” tests in gateway/redis_receipt_store_test.go / gateway/config_test.go cover receipt TTL/config fallback, not the middleware timeout logic.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gateway/middleware.go` around lines 163 - 167, Add unit tests for
RequestTimeoutMiddleware to cover the two missing branches: (1) timeout <= 0
behavior — create a request with timeout set to 0 and a handler that
intentionally sleeps longer than a positive timeout would; assert the middleware
does NOT cancel the request (handler completes and response is returned),
verifying the middleware uses c.Request.Context() and the no-op cancel
(referencing RequestTimeoutMiddleware and the branch where ctx =
c.Request.Context(); cancel = func() {}); (2) earlier parent-deadline behavior —
create a parent request Context with an earlier deadline than the middleware
timeout and wrap it through RequestTimeoutMiddleware, then assert the request is
canceled based on the parent deadline (handler sees context.Deadline exceeded)
rather than the middleware timeout, verifying middleware respects existing
parent deadlines. Ensure tests live alongside gateway/timeout_test.go and use
the same test utilities for injecting timeouts and inspecting cancellation
behavior.

@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
microai-paygate Ready Ready Preview, Comment Jun 5, 2026 2:21pm

@AnkanMisra

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gateway/middleware.go
Comment on lines 163 to +167
if timeout <= 0 {
// Preserve the existing behavior for zero/negative values.
ctx, cancel = context.WithTimeout(c.Request.Context(), timeout)
// Zero/negative timeout means no timeout — use the existing context
// without wrapping it, so requests don't cancel immediately.
ctx = c.Request.Context()
cancel = func() {} // no-op to avoid nil cancel panic in defer

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Add regression tests for the new timeout branches

Issue #196 explicitly requires coverage for timeout == 0, negative durations, and the earlier-parent-deadline path, but this change only updates gateway/middleware.go

the current timeout tests still cover only the positive timeout, fasthandler, and panic cases

That leaves the exact branches introduced here unprotected, so the immediate-504 regression or deadline-preservation behavior can change again without any failing test

@AnkanMisra AnkanMisra added bug Something isn't working go Pull requests that update go code level:intermediate Moderate scope requiring project familiarity or cross-file changes. type:bug A defect or regression in existing behavior. labels Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working go Pull requests that update go code level:intermediate Moderate scope requiring project familiarity or cross-file changes. type:bug A defect or regression in existing behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: RequestTimeoutMiddleware immediately times out on non-positive timeout

2 participants