Skip to content

feat: add exit code 3 for rate limiting (DIS-146)#36

Merged
ckorhonen merged 2 commits intomainfrom
devin/DIS-146-1772640278
Mar 4, 2026
Merged

feat: add exit code 3 for rate limiting (DIS-146)#36
ckorhonen merged 2 commits intomainfrom
devin/DIS-146-1772640278

Conversation

@ckorhonen
Copy link
Collaborator

@ckorhonen ckorhonen commented Mar 4, 2026

feat: add dedicated exit code 3 for rate limiting (DIS-146)

Summary

When the OpenSea API returns HTTP 429, the CLI now exits with code 3 and labels the error as "Rate Limited" instead of the generic "API Error". Previously, 429s were indistinguishable from other API errors (both exited with code 1), making it impossible for agents to programmatically detect rate limits and implement wait-and-retry.

Exit codes after this change: 0 success · 1 API error (non-429) · 2 auth error · 3 rate limited

Files changed:

  • src/cli.ts — conditional check on statusCode === 429 in the error handler; all exit codes use named constants (EXIT_API_ERROR, EXIT_AUTH_ERROR, EXIT_RATE_LIMITED)
  • README.md / .agents/rules.md — updated exit code documentation
  • 3 new test files covering the 429, non-429 API error, and network error exit paths

Updates since last revision

Addressed reviewer feedback from @ckorhonen:

  1. Extracted exit code constants — EXIT_API_ERROR = 1, EXIT_AUTH_ERROR = 2, EXIT_RATE_LIMITED = 3 — replacing all magic numbers across the three process.exit() call sites
  2. Clarified README exit code 1 as API error (non-429) to make the 429/non-429 split explicit

Review & Testing Checklist for Human

  • Verify the isRateLimited conditional in src/cli.ts correctly branches on statusCode === 429 only — no other 4xx codes should trigger exit code 3
  • Manual test: build the CLI, hit a rate-limited endpoint (or mock one), and confirm stderr output shows "error": "Rate Limited" with exit code 3
  • If PR feat: add opensea health diagnostic command (DIS-144) #35 (health command) merges alongside this, check that a rate-limited health check also uses exit code 3 — currently the health command catches errors internally and always exits 1

Notes

  • Confidence: 🟢 HIGH — small logic change with clear semantics; all 154 tests pass
  • Test files are split across 3 files because cli.ts executes main() at import time, requiring separate module scopes per error scenario
  • Linear ticket: DIS-146
  • Devin session
  • Requested by: @ckorhonen

Open with Devin

When the OpenSea API returns HTTP 429, the CLI now exits with code 3
and labels the error as 'Rate Limited' instead of the generic 'API Error'.
This allows agents to programmatically detect rate limits and implement
wait-and-retry behavior.

Exit codes: 0=success, 1=API error, 2=auth error, 3=rate limited.

Co-Authored-By: Chris K <ckorhonen@gmail.com>
@devin-ai-integration
Copy link
Contributor

Original prompt from Chris K
Please work on ticket "[opensea-cli] Add dedicated exit code 3 for rate limiting" ([DIS-146](https://linear.app/opensea/issue/DIS-146/opensea-cli-add-dedicated-exit-code-3-for-rate-limiting))

PLAYBOOK_md:
# Ticket to PR

## Overview

This playbook guides the process of taking a Linear ticket from initial scoping through implementation to final review. The workflow ensures proper context gathering, quality implementation, and thorough code review before delivery. The agent uses the Linear MCP to manage ticket status and communication throughout.

## What's Needed From User

- Linear ticket URL or ticket ID (e.g., `ENG-123` or `https://linear.app/team/issue/ENG-123/...`)
- Repository access for the codebase where changes will be made

<phase name="Disambiguation" id="1">
## Disambiguation Phase

Think about the full user intent. Tickets are sometimes sparse. Make sure you disambiguate to the full scope that the user intended.

1. Fetch the ticket details using the Linear MCP `get_issue` tool with the ticket ID
2. Before diving into code: use the devin MCP to get a high-level understanding of the relevant systems and architecture. Use `ask_question` to learn about the relevant systems – send queries for multiple repos that could be relevant to get the full picture. Use `read_wiki_contents` to then get a better understanding how different parts of the codebase connect to each other.
3. Gather additional context to understand what the ticket means and refers to:
   - Look at past tickets in the same project and from the same author to understand patterns and terminology
   - Search for related commits and PRs (by author and content) that may provide context on the affected systems
   - Check any linked documents, designs, or parent tickets
   - Investigate the actual code
4. Identify any ambiguity in what the ticket refers to or asks for, including jargon or project-specific terms and use all means necessary to answer this yourself
5. Consult your smart friend: pass in ... (8114 chars truncated...)

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@devin-ai-integration devin-ai-integration bot marked this pull request as ready for review March 4, 2026 16:12
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Copy link
Collaborator Author

@ckorhonen ckorhonen left a comment

Choose a reason for hiding this comment

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

Code Review

Overall: Approve with suggestions — All 154 tests pass. The implementation is correct — the conditional is properly nested and JSON output includes all diagnostic fields.

Suggestions

  1. Clarify README exit codes — Update 1 - API error to 1 - API error (non-429) to make clear that 429s use code 3.

  2. Extract exit code constants — With three exit codes (1, 2, 3) as magic numbers, consider defining named constants:

    const EXIT_API_ERROR = 1
    const EXIT_AUTH_ERROR = 2
    const EXIT_RATE_LIMITED = 3
  3. Test fragility note — The 3 separate test files rely on Vitest's module isolation because main() auto-executes on import. If isolate: false is ever set, all 3 break. Future improvement: refactor cli.ts to export main() without auto-executing.

  4. Interaction with PR #35 — The health command catches errors internally and always exits with code 1. A rate-limited health check won't use exit code 3. Worth aligning if both PRs merge.

I've pushed a commit with fixes 1 and 2.

Address reviewer feedback:
- Extract EXIT_API_ERROR, EXIT_AUTH_ERROR, EXIT_RATE_LIMITED constants
- Clarify README exit code 1 as 'API error (non-429)'

Co-Authored-By: Chris K <ckorhonen@gmail.com>
@devin-ai-integration
Copy link
Contributor

Addressed suggestions 1 and 2 in bd01192:

  • Extracted EXIT_API_ERROR, EXIT_AUTH_ERROR, EXIT_RATE_LIMITED constants — all three process.exit() call sites now use them
  • Updated README exit code 1 to "API error (non-429)"

Re: suggestion 3 (test fragility) — agreed, exporting main() separately would be a cleaner test boundary. Left as-is for this PR since it's a refactor of existing structure.

Re: suggestion 4 (PR #35 interaction) — good call. If both merge, the health command's internal catch should be updated to use EXIT_RATE_LIMITED for 429s. Happy to follow up in a separate PR once #35 lands.

@ckorhonen ckorhonen merged commit eb3ef4a into main Mar 4, 2026
5 checks passed
@ckorhonen ckorhonen deleted the devin/DIS-146-1772640278 branch March 4, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants