fix(a2a): change A2aErrorDetail.code to int and map error constants to JSON-RPC 2.0 integer codes #3310
No reviewers
Labels
No labels
auto/needs-reevaluation
controller-managed
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Reference
cleveragents/cleveragents-core!3310
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/a2a-error-codes-integer-jsonrpc2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Fixes #2746 —
A2aErrorDetail.codewas typed asstrand error constants were string literals, violating the JSON-RPC 2.0 specification which requires error codes to be integers.Problem
External A2A-compliant clients received
{"code": "NOT_FOUND", ...}instead of the required{"code": -32001, ...}, breaking interoperability with any standards-conformant JSON-RPC 2.0 client.Changes
Core Type Fix
src/cleveragents/a2a/models.py: ChangedA2aErrorDetail.codefromstrtoint; updatedfield_validatorto only validatemessagefield (Pydantic enforces int type forcode)src/cleveragents/a2a/errors.py: Changed all error code constants from string literals to JSON-RPC 2.0 integer codes perdocs/reference/a2a.mdtaxonomy:NOT_FOUND = -32001AUTH_ERROR = -32002FORBIDDEN = -32003INVALID_STATE = -32004PLAN_ERROR = -32008CONFIGURATION_ERROR = -32009VALIDATION_ERROR = -32602INTERNAL_ERROR = -32603map_domain_error()return type fromtuple[str, str]totuple[int, str]Test Updates
features/steps/a2a_facade_steps.py: UpdatedA2aErrorDetailconstruction to map symbolic string names to integer codes via_CODE_MAPfeatures/steps/a2a_facade_wiring_steps.py: Updated error code assertion to map symbolic names to integers for comparisonfeatures/steps/a2a_facade_coverage_boost_steps.py: Same as abovefeatures/steps/a2a_jsonrpc_wire_format_steps.py: Updated allA2aErrorDetailconstructions and JSON-RPC dict payloads to use integer codesrobot/helper_a2a_facade_wiring.py: Updatedwired_error_mapping()to compare against integer codesrobot/helper_a2a_jsonrpc_wire_format.py: Updatedresponse_error_wire_format()to use integer code-32001instead of string"NOT_FOUND"Design Decisions
Backward-compatible step definitions: Feature files continue to use symbolic names like
"NOT_FOUND"in Gherkin scenarios. Step definitions map these to integer codes via_CODE_MAP, avoiding mass feature file changes while maintaining readability.Integer taxonomy follows spec: The integer values follow the JSON-RPC 2.0 standard codes (-32700 to -32600) for standard errors, and the application-defined range (-32001 to -32009) for domain-specific errors as documented in
docs/reference/a2a.md.CONFIGURATION_ERRORassigned -32009: Not in the standard JSON-RPC 2.0 range but follows the application-defined pattern from the spec taxonomy.Quality Gates
nox -e typecheck: ✅ 0 errors, 0 warningsCloses #2746
Automated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
🔒 Review claimed by reviewer-pool-1 [claim-token: reviewer-pool-1-3310-1775375100]
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer
🔍 Code Review — REQUEST CHANGES
Reviewed PR #3310 with focus on test-coverage-quality, test-scenario-completeness, and test-maintainability.
The core change — converting
A2aErrorDetail.codefromstrtointand mapping error constants to JSON-RPC 2.0 integer codes — is correct and well-motivated. The JSON-RPC 2.0 specification (Section 5.1) requires error codes to be integers, and this fix brings the implementation into compliance. The integer values chosen align with the taxonomy documented indocs/reference/a2a.md.However, I found issues in test maintainability and spec alignment that should be addressed before merge.
Required Changes
1. [TEST-MAINTAINABILITY]
_CODE_MAPduplicated 6 times across 4 step filesLocations:
features/steps/a2a_facade_steps.py— defined twice (instep_create_responseandstep_create_error_detail)features/steps/a2a_facade_wiring_steps.py— defined once (instep_wired_error_code)features/steps/a2a_facade_coverage_boost_steps.py— defined once (instep_cb_error_code)features/steps/a2a_jsonrpc_wire_format_steps.py— defined twice (instep_create_error_responseandstep_jsonrpc_error_response_dict)Issue: The identical
_CODE_MAPdictionary mapping symbolic error names to integer codes is copy-pasted 6 times. If a new error code is added (e.g.,-32005forDuplicateEntityError, which is already in the spec but not in the constants), or an existing code value changes, all 6 copies must be updated in lockstep. This is a high-risk maintenance burden and violates DRY principles.Required: Extract
_CODE_MAPto a single shared location. Options:features/steps/_a2a_test_helpers.py)cleveragents.a2a.errorsmodule constants, e.g.:Then import and use this single definition in all step files.
2. [SPEC]
CONFIGURATION_ERROR = -32009not documented indocs/reference/a2a.mdsrc/cleveragents/a2a/errors.py—CONFIGURATION_ERROR: int = -32009docs/reference/a2a.mdonly goes up to-32008(PlanError). The code-32009forConfigurationErroris not listed in the spec reference. This creates a discrepancy between the code and the specification — which per project rules is the source of truth.-32009 | ConfigurationError | Configuration errorto the Error Code Taxonomy table indocs/reference/a2a.md, ORNon-blocking Suggestions
3. [TEST-COVERAGE] Missing test coverage for
AuthenticationError,AuthorizationError, andConfigurationErrormappingsThe
wired_error_mapping()function inrobot/helper_a2a_facade_wiring.pytests the mapping forResourceNotFoundError,ValidationError,PlanError, andBusinessRuleViolation, but does not testAuthenticationError → AUTH_ERROR (-32002),AuthorizationError → FORBIDDEN (-32003), orConfigurationError → CONFIGURATION_ERROR (-32009). Similarly, the Behave wiring steps only exercise NOT_FOUND, VALIDATION_ERROR, PLAN_ERROR, and INVALID_STATE error paths. Consider adding scenarios for the remaining mappings to ensure complete coverage of themap_domain_error()function.4. [TEST-QUALITY] Silent fallback in
_CODE_MAP.get()could mask test bugsIn several step definitions, the fallback
_CODE_MAP.get(code, _a2a_errors.INTERNAL_ERROR)silently maps any unknown symbolic name toINTERNAL_ERROR. If a feature file has a typo (e.g.,"NOTFOUND"instead of"NOT_FOUND"), the test would silently use-32603instead of failing. Consider using a strict lookup that raisesKeyErrorfor unknown names, or at minimum logging a warning.5. [TEST-QUALITY] Pre-existing
# type: ignorecomments in test filesfeatures/steps/a2a_facade_wiring_steps.pyandfeatures/steps/a2a_jsonrpc_wire_format_steps.pycontain# type: ignore[assignment,misc]in theirImportErrorfallback blocks. Per CONTRIBUTING.md,# type: ignoresuppressions are forbidden. These are pre-existing (not introduced by this PR), but worth noting for a follow-up cleanup.Good Aspects
"NOT_FOUND") while step definitions handle the mapping — excellent for test readabilitycodefrom thefield_validatoris correct since Pydantic enforcesinttype nativelyDecision: REQUEST CHANGES 🔄
The two required changes (extracting duplicated
_CODE_MAPand documenting-32009in the spec) should be addressed before merge. The core implementation is sound.Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
🔍 Code Review — Architecture, Module Boundaries & Interface Contracts
Reviewed PR #3310 with focus on architecture-alignment, module-boundaries, and interface-contracts.
Overview
This PR correctly fixes a JSON-RPC 2.0 specification compliance defect:
A2aErrorDetail.codewas typed asstrwith string literal constants, but the JSON-RPC 2.0 specification (Section 5.1) mandates integer error codes. The fix changes the type tointand maps all error constants to their proper integer values from thedocs/reference/a2a.mdtaxonomy.The core implementation is sound. I'm adding architecture-focused observations that complement the previous review's test-quality findings.
Architecture Alignment
1. ✅ JSON-RPC 2.0 Wire Format Compliance — Correct
The integer code assignments align with the spec taxonomy in
docs/reference/a2a.md:NOT_FOUND-32001AUTH_ERROR-32002FORBIDDEN-32003INVALID_STATE-32004PLAN_ERROR-32008VALIDATION_ERROR-32602INTERNAL_ERROR-32603CONFIGURATION_ERROR-320092. ⚠️ [SPEC GAP]
CONFIGURATION_ERROR = -32009— Not indocs/reference/a2a.mdsrc/cleveragents/a2a/errors.py:44docs/reference/a2a.mdends at-32008(PlanError). The code-32009forConfigurationErroris not listed. The code's own comment block (lines 26-39) documents it, but the authoritative spec reference does not.docs/reference/a2a.mdError Code Taxonomy table to include| -32009 | ConfigurationError | Configuration error |, or file a follow-up issue to track the spec update.3. ⚠️ [SPEC GAP] Missing error code constants for spec-defined codes
The spec defines three additional error codes that have no corresponding constants in
errors.py:-32005→DuplicateEntityError-32006→BudgetExceededError-32007→A2aVersionMismatchErrorThese are not blocking for this PR (which is scoped to fixing the str→int type), but they represent an incomplete mapping between the spec taxonomy and the code. Consider filing a follow-up issue.
Interface Contracts
4. ✅
map_domain_error()return type correctly updatedtuple[str, str]tuple[int, str]This is a breaking change to the public API, but it's the correct fix. The function is exported in
__all__and its docstring is properly updated to document the new integer return type. All callers within the codebase are updated.5. ✅
A2aErrorDetail.codetype change — Correct Pydantic approachcode: strwith@field_validator("code", "message")checking non-emptycode: intwith@field_validator("message")onlyRemoving
codefrom the field validator is correct — Pydantic v2 enforces theinttype annotation natively, so the non-empty string check is no longer applicable. Themodel_config = ConfigDict(strict=False)allows coercion from compatible types (e.g.,float→int), which is appropriate for JSON deserialization.6. ℹ️ [DOCSTRING]
A2aErrorDetaildocstring is slightly misleadingsrc/cleveragents/a2a/models.py,A2aErrorDetailclass docstringModule Boundaries
7. ⚠️ [DRY]
_CODE_MAPduplicated across module boundariesLocations: 6 definitions across 4 step files:
features/steps/a2a_facade_steps.py(×2:step_create_response,step_create_error_detail)features/steps/a2a_facade_wiring_steps.py(×1:step_wired_error_code)features/steps/a2a_facade_coverage_boost_steps.py(×1:step_cb_error_code)features/steps/a2a_jsonrpc_wire_format_steps.py(×2:step_create_error_response,step_jsonrpc_error_response_dict)Issue: This creates a coupling problem: the mapping between symbolic names and integer codes is now a de facto interface contract between the feature files and the step definitions, but it's defined 6 times with no single source of truth. If a code value changes or a new code is added, all 6 copies must be updated in lockstep.
Architectural recommendation: Extract to a shared test helper that derives the mapping programmatically from
cleveragents.a2a.errors:This ensures the test mapping always stays in sync with the source module.
8. ✅ Robot Framework helpers correctly updated
Both
robot/helper_a2a_facade_wiring.pyandrobot/helper_a2a_jsonrpc_wire_format.pyare properly updated:wired_error_mapping()now compares against integer codes from the errors moduleresponse_error_wire_format()uses-32001directly instead of"NOT_FOUND"The Robot helpers import from
cleveragents.a2a.errorsdirectly rather than duplicating the mapping, which is the correct pattern.CONTRIBUTING.md Compliance
fix(a2a): change A2aErrorDetail.code to int...ISSUES CLOSED: #2746footerCloses #2746Type/Buglabel# type: ignoreintroducedPre-existing Issues (Not Introduced by This PR)
features/steps/a2a_facade_wiring_steps.pylines 20-22:# type: ignore[assignment,misc]in ImportError fallbackfeatures/steps/a2a_jsonrpc_wire_format_steps.pylines 24-27:# type: ignore[assignment,misc]in ImportError fallbackfeatures/steps/a2a_facade_coverage_boost_steps.pyline 82:# type: ignore[arg-type](intentional for testing TypeError path)These violate CONTRIBUTING.md but are pre-existing. Consider a follow-up cleanup issue.
Summary
The core implementation is architecturally sound — it correctly aligns the A2A error code interface with JSON-RPC 2.0 requirements. The interface contract changes (
str→int) are properly propagated through all layers (models, error constants, domain mapping, unit tests, integration tests).Items requiring attention before merge:
-32009— Updatedocs/reference/a2a.mdor file follow-up issue_CODE_MAPduplication — Extract to shared test helper to maintain module boundary hygieneNon-blocking suggestions:
3. Clarify
A2aErrorDetaildocstring re: implementation-defined vs application-defined codes4. Consider follow-up issue for missing spec codes (-32005, -32006, -32007)
5. Consider follow-up issue for pre-existing
# type: ignorein test step filesAutomated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
538158526cf5d244cd37Review Feedback Addressed
Both required changes from the REQUEST CHANGES review have been implemented and pushed (amended commit
f5d244cd).Required Change 1:
_CODE_MAPduplication → shared module ✅Extracted to
features/steps/_a2a_code_map.py— a new shared module that derives the mapping directly fromcleveragents.a2a.errorsconstants:All 4 step files now import
A2A_CODE_MAPfrom this single source of truth:features/steps/a2a_facade_steps.py— 2 inline_CODE_MAPdicts removedfeatures/steps/a2a_facade_wiring_steps.py— 1 inline_CODE_MAPdict removedfeatures/steps/a2a_facade_coverage_boost_steps.py— 1 inline_CODE_MAPdict removedfeatures/steps/a2a_jsonrpc_wire_format_steps.py— 2 inline_CODE_MAPdicts removedTotal: 6 duplicate definitions → 1 canonical definition.
Required Change 2:
CONFIGURATION_ERROR = -32009documented in spec ✅Added to the Error Code Taxonomy table in
docs/reference/a2a.md:The table now covers all 8 error constants defined in
errors.py.All files pass Ruff linting with zero violations.
Automated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
🔍 Code Review — APPROVED ✅
Reviewed PR #3310 with focus on api-consistency, naming-conventions, and code-patterns.
This PR fixes a JSON-RPC 2.0 specification compliance defect where
A2aErrorDetail.codewas typed asstrwith string literal constants, but the JSON-RPC 2.0 specification (Section 5.1) mandates integer error codes. The fix correctly changes the type tointand maps all error constants to their proper integer values per thedocs/reference/a2a.mdtaxonomy.I note that a previous review (now stale) requested two changes — extracting duplicated
_CODE_MAPto a shared module and documenting-32009in the spec — and both have been addressed in the amended commit.Deep Dive: API Consistency ✅
A2aErrorDetail.code: int— Correctly matches JSON-RPC 2.0 Section 5.1 requirement. Themodel_config = ConfigDict(strict=False)appropriately allows coercion from compatible types during JSON deserialization.map_domain_error()return type — Properly updated fromtuple[str, str]totuple[int, str]. The docstring is updated to document the new integer return type. All callers are updated consistently.__all__exports — Botherrors.pyandmodels.pyhave complete__all__lists covering all public symbols. The 8 error code constants, 4 exception classes, andmap_domain_errorare all exported.docs/reference/a2a.mdError Code Taxonomy table (which now includes-32009forConfigurationError).("code", "message")to("message")only, since Pydantic v2 enforces theinttype annotation natively. The non-empty string check is no longer applicable to an integer field.Deep Dive: Naming Conventions ✅
UPPER_SNAKE_CASEconsistently:NOT_FOUND,AUTH_ERROR,FORBIDDEN,INVALID_STATE,PLAN_ERROR,CONFIGURATION_ERROR,VALIDATION_ERROR,INTERNAL_ERROR.PascalCasewithA2aprefix:A2aError,A2aNotAvailableError,A2aVersionMismatchError,A2aOperationNotFoundError. Consistent with project conventions._a2a_code_map.pyuses underscore prefix appropriately for an internal module. The constantA2A_CODE_MAPusesUPPER_SNAKE_CASEcorrectly for a module-level constant.int), all function signatures are fully typed, and return types are specified.Deep Dive: Code Patterns ✅
features/steps/_a2a_code_map.pyderives values directly fromcleveragents.a2a.errorsconstants rather than hardcoding integers. This ensures the test mapping stays in sync with the source module automatically. Excellent pattern.A2A_CODE_MAP.get(code, int(code) if code.lstrip("-").isdigit() else _a2a_errors.INTERNAL_ERROR)handles both symbolic names from Gherkin and raw integer strings gracefully. Consistent across all step files.map_domain_error()precedence chain — The isinstance chain correctly checks specific exception types before their base classes (e.g.,PlanErrorbeforeBusinessRuleViolation,CleverAgentsErroras final catch-all). This prevents incorrect classification."NOT_FOUND") while step definitions handle the mapping. This preserves test readability without coupling feature files to implementation details.Standard Criteria
fix(a2a): change A2aErrorDetail.code to int...ISSUES CLOSED: #2746footerCloses #2746Type/Buglabel# type: ignoreintroduceddocs/reference/a2a.mdupdated with-32009Minor Suggestions (Non-blocking)
Duplicate
A2aResponseconstruction infeatures/steps/a2a_jsonrpc_wire_format_steps.py— Instep_create_error_response(),context.responseis assigned twice with identical values:The second assignment overwrites the first with the same value. This is harmless but appears to be a copy-paste artifact. Consider removing the duplicate.
A2aErrorDetaildocstring precision — The docstring states "Application-defined codes must be outside this range" (referring to -32768 to -32000), but the codes used (-32001 to -32009) are within this range as "implementation-defined server errors" per JSON-RPC 2.0. This is technically correct usage but the docstring wording could be more precise. Non-blocking.Missing error code constants for spec-defined codes — The spec defines
-32005(DuplicateEntityError),-32006(BudgetExceededError), and-32007(A2aVersionMismatchError) but these don't have corresponding constants inerrors.py. This is a pre-existing gap outside the scope of this PR, but worth a follow-up issue.Pre-existing
# type: ignorein test files —a2a_facade_wiring_steps.pyanda2a_jsonrpc_wire_format_steps.pycontain# type: ignore[assignment,misc]in theirImportErrorfallback blocks. These are pre-existing (not introduced by this PR) but violate CONTRIBUTING.md. Worth a follow-up cleanup.Decision: APPROVED ✅
The implementation is correct, the API changes are consistent, naming follows project conventions, and code patterns are clean. The previous review's required changes have been properly addressed. The core
str→inttype change is well-propagated through all layers (models, error constants, domain mapping, unit tests, integration tests, spec documentation).Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer