Skip to content

fix(chat-migration): preserve full v1 serialized error when migrating to v2#16087

Open
EurFelux wants to merge 3 commits into
mainfrom
eurfelux/v2/fix/chat-migrator-error-fields
Open

fix(chat-migration): preserve full v1 serialized error when migrating to v2#16087
EurFelux wants to merge 3 commits into
mainfrom
eurfelux/v2/fix/chat-migrator-error-fields

Conversation

@EurFelux

Copy link
Copy Markdown
Collaborator

What this PR does

Before this PR:

  • The v1→v2 chat migrator (ChatMappings.transformSingleBlockToPart) converted a v1 ErrorMessageBlock into a v2 data-error part keeping only name and message, discarding every other field v1's serializeError attaches to the error.
  • So after migrating from v1, every old error message lost the fields the v2 renderer relies on: statusCode/status and providerId (used by classifyError), i18nKey (translated display), and finishReason/responseBody/data/url/stack/cause/etc. (used by ErrorDetailModal). Migrated errors therefore classified as unknown — triggering the AI-diagnosis fallback (a generic "An error occurred" placeholder that only later swaps to an AI-generated summary) — and showed almost nothing in the detail view.

After this PR:

  • The migrator spreads the full v1 serialized error into the data-error part (then defaults name/message to null), mirroring the live v2 persist path (PersistenceListener writes data: { ...error }). Migrated errors keep their statusCode / finishReason / i18nKey / providerId / detail fields, so classifyError resolves them immediately and ErrorDetailModal shows the real details.

Refs #16083

Why we need it and why it was done in this way

The following tradeoffs were made:

  • Spread the whole error object rather than enumerate fields: v1's serializeError can attach ~35 different fields (AI SDK error subtypes, i18nKey, providerId, ...), and v2's classifyError + ErrorBlock + ErrorDetailModal consume a broad subset of them. A field-by-field allow-list would be brittle and would silently drop future fields. Spreading matches the live persist path exactly.
  • oldBlock.error's runtime shape carries more than the narrow SerializedErrorData type models, so the fix relies on the spread (object spread copies all runtime own-props) plus an as ErrorPartData cast at the use site; the values are already JSON-safe (v1 serialized them).

The following alternatives were considered:

  • Keep cherry-picking specific fields (e.g. add statusCode, finishReason): rejected — incomplete and brittle vs. the renderer's broad consumption and future additions.

Breaking changes

None. Migrated data-error parts simply carry more fields; the v2 renderer already reads them when present.

Special notes for your reviewer

Checklist

  • PR: The PR description is expressive enough and will help future contributors
  • Code: Write code that humans can understand and Keep it simple
  • Refactor: You have left the code cleaner than you found it (Boy Scout Rule)
  • Upgrade: Impact of this change on upgrade flows was considered and addressed if required
  • Documentation: A user-guide update was considered and is not required (internal migration behavior).
  • Self-review: I have reviewed my own code before requesting review from others

Release note

Fix: after migrating from v1, error messages now keep their full details and show the correct diagnosis instead of a generic "An error occurred".

… to v2

The v1->v2 chat migrator converted v1 ErrorMessageBlocks to v2 `data-error` parts keeping only `name`/`message`, dropping every other field v1's `serializeError` attaches — `statusCode`/`status` and `providerId` (used by `classifyError`), `i18nKey` (translated display), and `finishReason`/`responseBody`/`data`/`url`/etc. (used by `ErrorDetailModal`).

As a result every migrated v1 error classified as 'unknown' on v2 — triggering the AI-diagnosis fallback (placeholder then delayed summary) — and lost its detail. Now spread the full serialized error, mirroring the live v2 persist path (`PersistenceListener` writes `data: { ...error }`).

Refs #16083

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: icarus <eurfelux@gmail.com>

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

LGTM

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