Feat: Port the typed-errors module (errors/) from adk-python - #583
Open
AmaadMartin wants to merge 2 commits into
Open
Feat: Port the typed-errors module (errors/) from adk-python#583AmaadMartin wants to merge 2 commits into
AmaadMartin wants to merge 2 commits into
Conversation
added 2 commits
July 30, 2026 11:08
Port the five error types from adk-python's errors/ package so TypeScript callers can branch on a failure mode with instanceof instead of matching error.message strings. All five extend Error directly. adk-python splits them across Exception and ValueError, but TypeScript has no ValueError analogue, so mirroring the split would invent a catch relationship that does not exist upstream. ToolErrorType keeps the exact adk-python member names and values because they are written into the OpenTelemetry error.type span attribute.
Pin the parity-critical surface: the default message of each error, the explicit .name, the flat hierarchy (each error is NOT an instance of a sibling), and the exact ToolErrorType member set, order and count. Every test imports from @google/adk so a missing barrel export fails the suite at import time.
Collaborator
|
Can you go though the whole app and create an essential list of all the places where we throw errors and what other error types we need to create? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
No existing issue.
2. Or, if no issue exists, describe the change:
Problem:
adk-js has no typed errors. Services signal "the thing you asked for does not exist", "the thing you are creating already exists" and "something genuinely broke" all with a bare
Error(or by returningundefined), so the only way for a caller to branch on the failure mode is to string-matcherror.message— which breaks the moment a message is reworded. adk-python solved this with a smallerrors/module; adk-js has no equivalent, so each in-flight port (session services, the evaluation framework) would otherwise invent its own incompatible error classes.Solution:
Port
adk-python/src/google/adk/errors/tocore/src/errors/, one TypeScript file per Python file, and export the six symbols fromcore/src/common.ts. The change is purely additive: 5 new source files, 5 new test files, and 8 added lines incommon.ts. No existingthrowsite is touched, no dependency is added, andgit diff main --statis 387 insertions / 0 deletions.core/src/errors/)not_found_error.tsNotFoundErrornot_found_error.pyalready_exists_error.tsAlreadyExistsErroralready_exists_error.pysession_not_found_error.tsSessionNotFoundErrorsession_not_found_error.pyinput_validation_error.tsInputValidationErrorinput_validation_error.pytool_execution_error.tsToolErrorType,ToolExecutionErrortool_execution_error.pyParity decisions, and which rule won each one. Every default message, member name and member value was diffed field-by-field against the Python source; local TS convention was only allowed to win where nothing crosses a process boundary.
NotFoundError/AlreadyExistsErrorextendExceptionwhileSessionNotFoundError/InputValidationErrorextendValueError("for backward compatibility" with callers already catchingValueError). TypeScript has noValueErroranalogue, so all five extendErrordirectly. Deliberately not done: makingSessionNotFoundError extend NotFoundError, or introducing anAdkErrorbase. Either would invent a catch relationship that does not exist upstream and would silently change whichcatchblock wins. A test in each file pins this by asserting an instance is not an instance of a sibling.error_type→errorType— local convention wins. The property name never leaves the process. The enum values are unchanged, because those do cross the boundary: they populate the OpenTelemetryerror.typespan attribute. All nine member names/values are byte-identical totool_execution_error.py, and a test asserts the exact list, its order, and a length of exactly 9 (so an added member also fails).error_type.value if isinstance(error_type, ToolErrorType) else error_typebecause an enum member is not its value. A TypeScript string-enum member is its string value at runtime, so direct assignment is already equivalent and the branch would be dead code. Pinned by a test assertingnew ToolExecutionError('x', ToolErrorType.NOT_FOUND).errorType === 'NOT_FOUND'.messagefield. Python setsself.messagebecauseExceptiondoes not expose one; JSErroralready has.messagefromsuper(message). A redundant field would shadow the built-in.Object.setPrototypeOf(this, X.prototype). That shim is only needed whenclassis downlevelled to an ES5 constructor function, and this repo never does that:tsconfig.jsonsets"target": "ES2020", andcore/build.js:9-12sets esbuild targetsnode10.4for the Node build andchrome58/firefox57/safari11for the browser build — all emit native classes. Verified on the actual build output:core/dist/web/errors/not_found_error.js(the most aggressive target) contains a literalclass NotFoundError extends Error, and importing it under Node givesinstanceof NotFoundError: true | instanceof Error: true | not sibling: true | name: NotFoundError. There are also zero occurrences ofsetPrototypeOfanywhere incore/src,dev/srcorintegrations/src, so adding it to five classes would be unexplained ceremony.errorTypeis written but not yet read inside this repo — intentional.core/src/telemetry/tracing.tshas noerror.typespan-attribute handling today; porting adk-python'sresolve_error_typeis separate follow-up work. This is a public API property being ported for parity, not dead internal config, so please don't read it as an unwired knob. Refactoring existing bare-Errorthrows (e.g.core/src/sessions/database_session_service.ts) is likewise deliberately out of scope — it would collide with the in-flight session/evaluation work.Barrel placement.
core/srchas no per-directoryindex.ts;core/src/common.tsis the real barrel and is re-exported wholesale by bothindex.ts(Node) andindex_web.ts(browser). These classes are platform-neutral, so they go incommon.tsonly — adding them toindex.tsas well would be redundant. Exported as values (export {…}, notexport type {…}), since they are runtime classes plus a runtime enum.Collision check. No path containing
errorsis touched by any other open PR. The only shared file iscore/src/common.ts, where a handful of open PRs add unrelated export lines; this PR is branched frommainrather than stacked on any of them, so expect at most a trivial add/add conflict incommon.tsdepending on merge order.Testing Plan
Please describe the tests that you ran to verify your changes.
Unit Tests:
5 new files under
core/test/errors/, 34 tests, all passing:Every test imports from
@google/adkrather than a relative path, so a missing or misspelt barrel export fails the suite at import time.Coverage: 100% statements / branches / functions / lines on
core/src/errors/, measured with:npx vitest run --project unit:core core/test/errors --coverage --coverage.include='core/src/errors/**'No coverage-ignore pragmas, and no structure was compromised to reach the number.
Coverage is not proof, so each test file was mutation-tested — the source was broken and the suite confirmed red, then reverted:
not_found_error.ts: default message…not found.→…not found!defaults the message when none is supplied—expected 'The requested item was not found!' to be 'The requested item was not found.'already_exists_error.ts: delete thethis.name = 'AlreadyExistsError';linesets name—expected 'Error' to be 'AlreadyExistsError'session_not_found_error.ts:extends Error→extends NotFoundError(invent a hierarchy)is not an instance of a sibling error class—expected SessionNotFoundError: Session not found. to not be an instance of NotFoundErrorinput_validation_error.ts: drop the default parameter (message = 'Invalid input.'→message: string)defaults the message when none is supplied—expected '' to be 'Invalid input.'tool_execution_error.ts: remove theGATEWAY_TIMEOUTmembermatches the adk-python members, in declaration order—expected [ … …(6) ] to deeply equal [ … …(7) ]tool_execution_error.ts: drop thereadonlyparameter property soerrorTypeis never storedexpected undefined to be 'BAD_REQUEST',… 'NOT_FOUND',… '500'tool_execution_error.ts:BAD_GATEWAY = 'BAD_GATEWAY'→'BadGateway'expected 'BadGateway' to be 'BAD_GATEWAY'and the member-table assertioncommon.ts: delete theNotFoundErrorbarrel export lineTypeError: NotFoundError is not a constructorEdge cases covered explicitly:
new X('')yields''and not the default (onlyundefinedtriggers a TS default parameter),new X(undefined)yields the default, and a message containing$replacement metacharacters ("a $& b $' c") is stored verbatim with no sanitisation.No integration tests were added, and that is deliberate: nothing in the repo throws or catches these types yet, so an "integration" test could only re-assert the unit behaviour through more indirection. The real integration surface arrives with the follow-up ports.
No new suppressions anywhere in the diff — no
any,as any,@ts-expect-error,@ts-ignore,eslint-disable, or coverage-ignore pragma, in source or in tests (verified by greppinggit diff main -U0for all of them: zero hits).Manual End-to-End (E2E) Tests:
From the repo root, run the checks CI runs:
Then confirm the symbols really are public, against the built package rather than the source alias:
Constructing each of the four no-arg errors from the built package prints its parity default:
The requested item was not found.,The resource already exists.,Session not found.,Invalid input.One honest note on
npm run ts:check: it is not part ofvalidation.yamland is currently red (366 errors across 67 files). Every one of those 67 files is a file this PR does not touch, and there are zero errors incore/src/errors/orcore/test/errors/. This PR neither adds to nor fixes that backlog.Checklist