Skip to content

Feat/api error - #5

Merged
bobrykov merged 3 commits into
masterfrom
feat/api-error
May 4, 2026
Merged

Feat/api error#5
bobrykov merged 3 commits into
masterfrom
feat/api-error

Conversation

@bobrykov

@bobrykov bobrykov commented May 4, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 285a358b-bd86-48c6-bc57-c5b9ee3c1a75

📥 Commits

Reviewing files that changed from the base of the PR and between f41ecfc and 5420d63.

📒 Files selected for processing (3)
  • Cargo.toml
  • src/api_error.rs
  • src/message.rs
✅ Files skipped from review due to trivial changes (1)
  • src/message.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • Cargo.toml
  • src/api_error.rs

📝 Walkthrough

Walkthrough

Adds project formatting/lint configs and Makefile changes; marks several message and core predicate methods as const fn and #[must_use]; introduces a new src/api_error.rs module defining numeric ErrorCode, ApiError variants, many constructors/conversions, predicate helpers, a Result<T> alias, and comprehensive unit tests.

Changes

Configuration & Linting Enforcement

Layer / File(s) Summary
Config files
.clippy.toml, rustfmt.toml
Adds full Clippy and rustfmt project configurations with thresholds and style rules.
Dependency / Linting Toggle
Cargo.toml
Adds serde_repr = "0.1" dependency and a [lints.clippy] section enabling pedantic and denying multiple Clippy lints.
Build targets
Makefile
Adds lint target (runs cargo fmt --all), reorders ci dependencies to run fmt earlier, and updates .PHONY.

API Error Infrastructure

Layer / File(s) Summary
Data Shape / Types
src/api_error.rs
Adds #[repr(u16)] pub enum ErrorCode (stable numeric codes) and pub enum ApiError variants (API/auth/http/tool/config/io/json/internal/Interrupted/Other) with thiserror derives.
Classification & Helpers
src/api_error.rs
Implements ApiError::code() mapping to ErrorCode (keyword/status heuristics, context-overflow detection), is_context_overflow(), is_retryable(), and const category predicates (is_auth_error, is_config_error, is_io_error, is_tool_error).
Constructors & Conversions
src/api_error.rs
Adds ergonomic constructors (e.g., api, auth_invalid_key, http_with_status, tool_not_found, api_timeout, api_rate_limited, io_*) and from_hyper helper; From conversions for serde_json::Error and std::io::Error.
Integration / Export
src/lib.rs
Exports module via pub mod api_error; and updates crate docs references.
Tests / Validation
src/api_error.rs tests
Comprehensive unit tests for code classification, predicates, retry logic, numeric stability, JSON round-trips, From conversions, from_hyper, and Result<T> alias.

API Surface Small Adjustments

Layer / File(s) Summary
Const & must_use additions
src/message.rs
Marks Message::new, several MessagePart predicates/accessors, ToolResult::from_multipart, ToolResult::is_string, and ToolResultPart::image as #[must_use] pub const fn (unchanged logic).
AgentError tweak
src/core/error.rs
Fixes doc typo; changes tool_not_found truncation to use saturating_sub and iterator-based join; makes is_recoverable and is_cancelled #[must_use] pub const fn (logic unchanged).

Possibly Related PRs

  • feat: add message module #2 — modifies src/message.rs (Message/ToolResult helpers); related because this PR marks many of those methods const/#[must_use].
  • feat: add error.rs #3 — touches src/core/error.rs (AgentError implementation); related due to overlapping agent error message/predicate updates.

Poem

🐰
I nudged the lints and ordered the lines,
Gave errors numbers and tidy confines,
Const carrots gleam where methods rest,
Formatting hops—code dressed its best,
A little rabbit claps—well-pressed!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feat/api error' is partially related to the changeset, referring to the main API error module addition, but is vague and doesn't clearly convey what was changed or why. Consider a more descriptive title like 'Add centralized API error handling and linting configuration' that better summarizes the multiple changes including error hierarchy, formatting, and linting setup.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Add a detailed pull request description explaining the changes, their rationale, and how they improve the codebase (e.g., error handling, code quality, formatting standards).
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/api-error

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

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.

🧹 Nitpick comments (1)
src/api_error.rs (1)

571-573: ⚡ Quick win

Http and Io variants always map to a single code regardless of actual error type.

The code() method maps all ApiError::Http errors to HttpConnectionError and all ApiError::Io errors to IoReadError, even though the ErrorCode enum defines more specific codes like HttpRequestError, HttpResponseError, IoFileNotFound, and IoWriteError.

Consider applying similar keyword-based classification for these variants, or documenting that callers should use specific constructors to get accurate codes.

♻️ Example: Add keyword matching for Io errors
-            Self::Io(_) => ErrorCode::IoReadError,
+            Self::Io(e) => {
+                let msg = e.to_string().to_lowercase();
+                if e.kind() == std::io::ErrorKind::NotFound {
+                    ErrorCode::IoFileNotFound
+                } else if msg.contains("write") || msg.contains("permission denied") {
+                    ErrorCode::IoWriteError
+                } else {
+                    ErrorCode::IoReadError
+                }
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api_error.rs` around lines 571 - 573, The code path in the
ApiError::code() method currently maps ApiError::Http and ApiError::Io to
generic ErrorCode::HttpConnectionError and ErrorCode::IoReadError; update code()
so it inspects the inner error to pick a more specific ErrorCode (for example:
for ApiError::Io inspect the std::io::ErrorKind to return IoFileNotFound,
IoWriteError, etc.; for ApiError::Http inspect the inner HTTP error type/message
(request vs response vs connection) or known keywords to return
HttpRequestError, HttpResponseError, etc.), falling back to the generic codes if
no specific match is found; reference the ApiError::Http and ApiError::Io
variants and the code() method and map to the ErrorCode enum values accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/api_error.rs`:
- Around line 571-573: The code path in the ApiError::code() method currently
maps ApiError::Http and ApiError::Io to generic ErrorCode::HttpConnectionError
and ErrorCode::IoReadError; update code() so it inspects the inner error to pick
a more specific ErrorCode (for example: for ApiError::Io inspect the
std::io::ErrorKind to return IoFileNotFound, IoWriteError, etc.; for
ApiError::Http inspect the inner HTTP error type/message (request vs response vs
connection) or known keywords to return HttpRequestError, HttpResponseError,
etc.), falling back to the generic codes if no specific match is found;
reference the ApiError::Http and ApiError::Io variants and the code() method and
map to the ErrorCode enum values accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 00866bea-477b-4e31-a71a-b0d2dd2537ff

📥 Commits

Reviewing files that changed from the base of the PR and between fdd2481 and 08e3b2e.

📒 Files selected for processing (8)
  • .clippy.toml
  • Cargo.toml
  • Makefile
  • rustfmt.toml
  • src/api_error.rs
  • src/core/error.rs
  • src/lib.rs
  • src/message.rs

@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: 4

🧹 Nitpick comments (1)
Makefile (1)

1-1: ⚡ Quick win

Add conventional all and clean targets for better Makefile interoperability.

Line 1 updates .PHONY, and this is a good place to also include all and clean since some tooling expects them (also matches the static-analysis warning).

Proposed patch
-.PHONY: check test clippy fmt docs ci lint
+.PHONY: all clean check test clippy fmt docs ci lint
+
+all: ci
+
+clean:
+	cargo clean
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` at line 1, Update the Makefile to advertise and implement
conventional targets: add "all" and "clean" to the .PHONY list on the existing
.PHONY line and add corresponding "all:" (typically defaulting to the main build
or test target such as "test" or "check") and "clean:" targets that remove
generated artifacts (e.g., build outputs, temp files) so tools expecting
standard targets can operate; ensure the "all" target depends on the appropriate
default target (e.g., "check" or "test") and "clean" runs the cleanup commands
used elsewhere in the repo.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/api_error.rs`:
- Around line 548-573: The match arm in ApiError::code() currently collapses all
HTTP errors into ErrorCode::HttpConnectionError, making
HttpRequestError/HttpResponseError unreachable (e.g., errors created via
ApiError::http_with_status) and misclassifying retryability; update
ApiError::code() to inspect the Http variant payload (or parse the "HTTP
{status}:" shape) inside the Self::Http(_) arm so that status-based mapping
returns ErrorCode::HttpRequestError for 4xx (client) responses and
ErrorCode::HttpResponseError for 5xx (server) responses (and preserve
HttpConnectionError for transport-level failures), ensuring is_retryable() sees
the correct ErrorCode; reference ApiError::http_with_status, the Self::Http(_)
match arm, ErrorCode::HttpRequestError, ErrorCode::HttpResponseError, and
is_retryable() when implementing.
- Around line 574-586: The error classification in the ApiError::Tool arm
(Self::Tool(msg)) uses substring matches on msg.to_lowercase(), causing
misclassification (e.g., "file not found" treated as ToolNotFound); update the
matching to check for constructor-shaped prefixes with starts_with on the
lowercased string (e.g., starts_with("tool not found:"), starts_with("permission
denied:"), starts_with("timeout:"), starts_with("invalid input:") ) and fall
back to ErrorCode::ToolExecutionFailed when none of those prefixes match; keep
the lowercase normalization (msg_lower) and only replace contains(...) checks
with starts_with(...) using the specific prefix patterns in the Self::Tool match
arm.
- Around line 97-98: The ErrorCode enum is currently derived with serde
Serialize/Deserialize so it serializes to variant names; change it to serialize
as numeric discriminants by adding #[repr(u16)] to the enum and replacing serde
derives with serde_repr ones: remove Serialize and Deserialize from the derive
and instead add #[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Copy,
PartialEq, Eq, Hash)] (from the serde_repr crate) so ErrorCode (the enum)
serializes/deserializes as its numeric value (e.g., 1000) rather than the
variant name.
- Around line 437-443: The Io variant currently converts any std::io::Error into
a single ErrorCode (IoReadError) making IoFileNotFound and IoWriteError
unreachable; add explicit constructors on ApiError such as
ApiError::io_file_not_found(std::io::Error), ApiError::io_read(std::io::Error),
and ApiError::io_write(std::io::Error) (or alternately accept an operation-kind
enum alongside the source error) and ensure each constructor sets the correct
error code (ErrorCode::IoFileNotFound, ErrorCode::IoReadError,
ErrorCode::IoWriteError) while storing the source std::io::Error so tests that
assert ApiError::IoFileNotFound or IoWriteError can construct those cases;
update places that relied on the #[from] conversion to call the appropriate
constructor or provide a converting function that maps ErrorKind::NotFound to
ApiError::io_file_not_found when context indicates a file access.

---

Nitpick comments:
In `@Makefile`:
- Line 1: Update the Makefile to advertise and implement conventional targets:
add "all" and "clean" to the .PHONY list on the existing .PHONY line and add
corresponding "all:" (typically defaulting to the main build or test target such
as "test" or "check") and "clean:" targets that remove generated artifacts
(e.g., build outputs, temp files) so tools expecting standard targets can
operate; ensure the "all" target depends on the appropriate default target
(e.g., "check" or "test") and "clean" runs the cleanup commands used elsewhere
in the repo.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b8d0ddf-171d-4f98-8d7a-4e4396309fb8

📥 Commits

Reviewing files that changed from the base of the PR and between 08e3b2e and f41ecfc.

📒 Files selected for processing (5)
  • Makefile
  • src/api_error.rs
  • src/core/error.rs
  • src/lib.rs
  • src/message.rs
✅ Files skipped from review due to trivial changes (2)
  • src/lib.rs
  • src/message.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/error.rs

Comment thread src/api_error.rs Outdated
Comment thread src/api_error.rs
Comment thread src/api_error.rs Outdated
Comment thread src/api_error.rs
Comment on lines +574 to +586
Self::Tool(msg) => {
let msg_lower = msg.to_lowercase();
if msg_lower.contains("not found") {
ErrorCode::ToolNotFound
} else if msg_lower.contains("permission") || msg_lower.contains("denied") {
ErrorCode::ToolPermissionDenied
} else if msg_lower.contains("timeout") {
ErrorCode::ToolTimeout
} else if msg_lower.contains("invalid") {
ErrorCode::ToolInputInvalid
} else {
ErrorCode::ToolExecutionFailed
}

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 | ⚡ Quick win

Tighten tool classification to the constructor prefixes instead of any substring match.

A generic execution failure like ApiError::tool_with_name("Read", "file not found") will currently classify as ToolNotFound, even though the tool exists and the missing thing is the file. Matching exact constructor-shaped prefixes such as starts_with("tool not found:") avoids turning arbitrary tool stderr into the wrong error code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api_error.rs` around lines 574 - 586, The error classification in the
ApiError::Tool arm (Self::Tool(msg)) uses substring matches on
msg.to_lowercase(), causing misclassification (e.g., "file not found" treated as
ToolNotFound); update the matching to check for constructor-shaped prefixes with
starts_with on the lowercased string (e.g., starts_with("tool not found:"),
starts_with("permission denied:"), starts_with("timeout:"), starts_with("invalid
input:") ) and fall back to ErrorCode::ToolExecutionFailed when none of those
prefixes match; keep the lowercase normalization (msg_lower) and only replace
contains(...) checks with starts_with(...) using the specific prefix patterns in
the Self::Tool match arm.

@bobrykov
bobrykov merged commit 5bcc6af into master May 4, 2026
5 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 4, 2026
@bobrykov
bobrykov deleted the feat/api-error branch July 1, 2026 06:35
bobrykov added a commit that referenced this pull request Aug 18, 2026
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.

1 participant