Skip to content

fix(node): make creation-route rate limit configurable; fix(gl): surface real errors on pr create failure#148

Open
carlitos973 wants to merge 1 commit into
Gitlawb:mainfrom
carlitos973:fix/configurable-rate-limit-and-pr-error-masking
Open

fix(node): make creation-route rate limit configurable; fix(gl): surface real errors on pr create failure#148
carlitos973 wants to merge 1 commit into
Gitlawb:mainfrom
carlitos973:fix/configurable-rate-limit-and-pr-error-masking

Conversation

@carlitos973

@carlitos973 carlitos973 commented Jul 3, 2026

Copy link
Copy Markdown

Two small, independent fixes found while building an autonomous agent on gitlawb:

1. Configurable creation-route rate limit (gitlawb-node)

The rate limiter was hardcoded to 10 requests/hour per DID, shared across the whole creation-routes group (repo create, issue create, PR create, register, fork):

let rate_limiter = rate_limit::RateLimiter::new(10, std::time::Duration::from_secs(3600));

This is workable for a human clicking around, but a real agent/bot doing modest volume (or retrying after a transient error) hits the wall fast, with no way to raise it short of recompiling. We hit this directly running an ACP-hired worker agent — it also caused a real job to expire mid-SLA while rate-limited.

This PR adds two config fields following the existing clap env-var pattern already used throughout Config:

  • GITLAWB_RATE_LIMIT_MAX (default 10, unchanged)
  • GITLAWB_RATE_LIMIT_WINDOW_SECS (default 3600, unchanged)

main.rs now builds the limiter from config instead of the hardcoded values. No behavior changes unless a node operator explicitly opts in.

2. gl pr create masks the real error on failure

let pr: Value = resp.json().await.context("invalid JSON")?;
if !status.is_success() {
    let msg = pr["message"].as_str().unwrap_or("unknown error");
    anyhow::bail!("create PR failed ({status}): {msg}");
}

Calling .json() before checking status means any non-2xx response with an empty or non-JSON body (e.g. a 404 from a malformed URL) crashes with a generic "invalid JSON" error instead of the real one. Fixed by reading the raw response body first and including it in the error message when the request fails.

Testing

Both changes verified with cargo check -p gitlawb-node -p gl — compiles clean, no warnings.

Summary by CodeRabbit

  • New Features

    • Added configurable request rate limits with defaults, allowing creation throttling to be adjusted via environment settings.
  • Bug Fixes

    • Improved create-request error messages so failures now include the HTTP status and full server response.
    • Preserved successful response handling while making error reporting clearer.

…ace real errors on pr create failure

- gitlawb-node: rate limiter was hardcoded to 10 req/hour per DID across all
  creation routes (repo/issue/PR create, register, fork). Adds
  GITLAWB_RATE_LIMIT_MAX and GITLAWB_RATE_LIMIT_WINDOW_SECS config fields
  (clap env-var pattern matching the rest of Config), defaults unchanged.
  Lets node operators tune this for real agent/bot usage without recompiling.

- gl pr create: previously called resp.json() before checking response
  status, so any non-2xx response with an empty or non-JSON body crashed
  with a generic "invalid JSON" error instead of surfacing the real cause.
  Now reads the raw response body first and includes it in the error message
  on failure.
Copilot AI review requested due to automatic review settings July 3, 2026 14:30
@github-actions github-actions Bot added needs-issue PR has no linked issue needs-tests Source changed without accompanying tests (advisory) labels Jul 3, 2026
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution. A couple of things will help us review this faster:

  • Link the issue this addresses (Closes #123). For protocol changes, open an issue first.
  • This changes Rust source but no tests changed. Tests are required for fixes and strongly encouraged for features.

See CONTRIBUTING.md. Update the PR and these notes will clear automatically.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds two configurable rate-limiting fields to node Config (max requests, window seconds) wired via CLI/env, uses them to construct the rate limiter in main(), and changes gl's PR create command to read response text first, erroring with the raw body on failure before parsing JSON.

Changes

Configurable Rate Limiting

Layer / File(s) Summary
Rate limit config fields
crates/gitlawb-node/src/config.rs
Adds rate_limit_max_requests and rate_limit_window_secs fields with clap/env wiring (GITLAWB_RATE_LIMIT_MAX, GITLAWB_RATE_LIMIT_WINDOW_SECS) and defaults (10, 3600).
Rate limiter initialization
crates/gitlawb-node/src/main.rs
main() builds the rate limiter from configured max requests and window duration instead of hardcoded values.

PR Create Error Handling

Layer / File(s) Summary
Raw text response handling
crates/gl/src/pr.rs
cmd_create reads the response as text first, fails with status and raw body on non-success, then parses JSON from that text on success.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Main
  participant Config
  participant RateLimiter
  Main->>Config: read rate_limit_max_requests, rate_limit_window_secs
  Main->>RateLimiter: new(max_requests, window_duration)
Loading

Related Issues: Not specified in the provided context.

Related PRs: Not specified in the provided context.

Suggested labels: enhancement, rate-limiting, cli

Suggested reviewers: Not specified in the provided context.

🐰 A window ticks, a limit set,
Config now holds what once was fret,
Errors bubble raw and true,
Text before JSON, a wiser view,
Hop along, the build is fresh anew.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed It clearly summarizes both code changes and stays concise.
Description check ✅ Passed It covers the two fixes, motivation, and verification, so the required substance is present despite not matching the template exactly.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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.

🧹 Nitpick comments (1)
crates/gitlawb-node/src/config.rs (1)

155-164: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider guarding against degenerate configured values.

If GITLAWB_RATE_LIMIT_WINDOW_SECS=0, rate_limit.rs's check() retains timestamps where duration_since(t) < self.window, so with a zero window this is always false — the timestamp count stays at 0 and the limiter never blocks anything, silently disabling rate limiting. Conversely, GITLAWB_RATE_LIMIT_MAX=0 makes len() >= max_requests always true, blocking all creation requests. Since these are now operator-configurable via env vars, a misconfiguration could unexpectedly disable protection or lock out all users.

Consider validating these values (e.g., panic/warn on 0) in Config construction or main().

🤖 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 `@crates/gitlawb-node/src/config.rs` around lines 155 - 164, The rate-limit
config fields can be set to degenerate values that either disable protection or
block all requests, so add validation for rate_limit_max_requests and
rate_limit_window_secs before the limiter is used. Guard Config construction or
main() against zero values for these settings, and fail fast or emit a clear
warning rather than letting rate_limit.rs::check() run with an unusable window
or max count.
🤖 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.

Nitpick comments:
In `@crates/gitlawb-node/src/config.rs`:
- Around line 155-164: The rate-limit config fields can be set to degenerate
values that either disable protection or block all requests, so add validation
for rate_limit_max_requests and rate_limit_window_secs before the limiter is
used. Guard Config construction or main() against zero values for these
settings, and fail fast or emit a clear warning rather than letting
rate_limit.rs::check() run with an unusable window or max count.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9cbeebc7-3682-4383-94ce-66168cdfdedc

📥 Commits

Reviewing files that changed from the base of the PR and between 563c456 and 15e017a.

📒 Files selected for processing (3)
  • crates/gitlawb-node/src/config.rs
  • crates/gitlawb-node/src/main.rs
  • crates/gl/src/pr.rs

Copilot AI left a comment

Copy link
Copy Markdown

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 makes two targeted improvements across the CLI and node daemon: (1) the node’s creation-route rate limiting becomes operator-configurable via existing clap/env config patterns, and (2) the gl pr create CLI surfaces the real HTTP failure body instead of masking it behind a JSON parse error.

Changes:

  • Make creation-route rate limit parameters configurable (max_requests, window_secs) instead of hardcoded constants.
  • Improve gl pr create error handling by reading the raw response body first and only parsing JSON on success.
  • Update node startup to construct the rate limiter from config.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
crates/gl/src/pr.rs Improves PR creation failure reporting by using raw response text and parsing JSON only on success.
crates/gitlawb-node/src/main.rs Builds the node’s rate limiter using configuration values instead of hardcoded defaults.
crates/gitlawb-node/src/config.rs Adds new clap/env configuration fields for creation-route rate limiting.

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

Comment thread crates/gl/src/pr.rs
Comment on lines +237 to 240
let raw_text = resp.text().await.context("failed to read response body")?;
if !status.is_success() {
let msg = pr["message"].as_str().unwrap_or("unknown error");
anyhow::bail!("create PR failed ({status}): {msg}");
anyhow::bail!("create PR failed ({status}): {raw_text}");
}
Comment thread crates/gl/src/pr.rs
Comment on lines 232 to +241
let resp = client
.post(&format!("/api/v1/repos/{owner}/{repo}/pulls"), &payload)
.await
.context("failed to connect to node")?;
let status = resp.status();
let pr: Value = resp.json().await.context("invalid JSON")?;

let raw_text = resp.text().await.context("failed to read response body")?;
if !status.is_success() {
let msg = pr["message"].as_str().unwrap_or("unknown error");
anyhow::bail!("create PR failed ({status}): {msg}");
anyhow::bail!("create PR failed ({status}): {raw_text}");
}
let pr: Value = serde_json::from_str(&raw_text).context("invalid JSON")?;
Comment on lines +156 to +160
/// Maximum number of creation requests (repo create, issue create, PR
/// create, register, fork) allowed per DID within the rate-limit window.
#[arg(long, env = "GITLAWB_RATE_LIMIT_MAX", default_value_t = 10)]
pub rate_limit_max_requests: u32,

Comment on lines +161 to +164
/// Rate-limit window size, in seconds, over which
/// `rate_limit_max_requests` applies.
#[arg(long, env = "GITLAWB_RATE_LIMIT_WINDOW_SECS", default_value_t = 3600)]
pub rate_limit_window_secs: u64,
Comment on lines +193 to +196
let rate_limiter = rate_limit::RateLimiter::new(
config.rate_limit_max_requests as usize,
std::time::Duration::from_secs(config.rate_limit_window_secs),
);
@beardthelion beardthelion added crate:gl gl — the contributor CLI crate:node gitlawb-node — the serving node and REST API kind:bug Defect fix — wrong or unsafe behavior labels Jul 3, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the update. I found one issue to clean up before this is ready.

Findings

  • [P3] Update the PR description to match the final migration
    PR description
    The current description still says migration v10 adds both the owner_did TEXT column and the idx_ref_updates_owner index, but the final patch intentionally dropped that index and the migration now only adds the nullable column. Since the index was deferred to the follow-up gate work, please update the PR body so reviewers and operators do not come away thinking this branch already ships that index.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crate:gl gl — the contributor CLI crate:node gitlawb-node — the serving node and REST API kind:bug Defect fix — wrong or unsafe behavior needs-issue PR has no linked issue needs-tests Source changed without accompanying tests (advisory)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants