Skip to content

refactor(portal-sdk, portal-framework-auth): improve error handling and processing for auth operations#481

Merged
pcfreak30 merged 1 commit intodevelopfrom
libs/portal-plugin-dashboard
Aug 31, 2025
Merged

refactor(portal-sdk, portal-framework-auth): improve error handling and processing for auth operations#481
pcfreak30 merged 1 commit intodevelopfrom
libs/portal-plugin-dashboard

Conversation

@pcfreak30
Copy link
Copy Markdown
Member

@pcfreak30 pcfreak30 commented Aug 31, 2025

  • Replace wrapErrorWithName with processApiError and createStandardError for more robust error handling
  • Add processValidationError helper to parse and clean validation error messages
  • Update AccountError class to include fields property for detailed validation errors
  • Enhance handleFetchError to extract error details and fields from various response formats
  • Handle unknown errors by returning existing AccountError instances unchanged

Summary by CodeRabbit

  • New Features

    • Validation-aware, user-friendly error messages in auth flows.
    • Field-level error details surfaced in error payloads and API responses.
    • Safer login redirects (sanitized/limited redirect targets).
    • No-content (204/205/304) responses treated as successful without a body.
  • Bug Fixes

    • More consistent error handling across login, registration, logout, password reset, and updates.
    • Improved parsing and preservation of original server error details; fewer false errors from empty or non-JSON responses.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Aug 31, 2025

⚠️ No Changeset found

Latest commit: 744f5ec

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 31, 2025

Walkthrough

Replaces simple error name-wrapping with validation-aware, multi-layer error processing in the auth data provider; adds redirect sanitization; simplifies SDK JSON parsing and empty-response detection; extends AccountError to carry structured fields and updates error extraction and serialization. Public signatures unchanged.

Changes

Cohort / File(s) Summary
Auth error processing & redirect sanitization
libs/portal-framework-auth/src/dataProviders/auth.ts
Replaced wrapErrorWithName with processApiError. Added processValidationError, createStandardError, processApiError, VALIDATION_ERROR_NAME, sanitizeRedirectUrl, and redirect constants (OTP_PATH, DASHBOARD_PATH). Updated login/OTP/register/logout/forgotPassword/updatePassword flows and catch blocks to use new helpers and preserve richer error metadata.
SDK fetch layer simplification
libs/portal-sdk/src/account.ts
Added isResponseEmpty(response). fetchJson now short-circuits empty responses, uses response.json() in try/catch, returns parsed payload as data: T on success, and delegates error mapping to handleFetchError/handleUnknownError. Removed manual raw-body parsing and multi-path backend-wrapped response handling.
SDK error types & extraction
libs/portal-sdk/src/types.ts
AccountError gains fields?: Record<string,string> and constructor updated to accept fields. Added normalizeFields and extractErrorDetails. handleFetchError now parses by content-type, extracts message/details/fields, constructs AccountError; handleUnknownError normalized inputs. toJSON() now returns { details, fields, message, statusCode }.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor UI as UI
  participant Auth as Auth Provider
  participant Proc as processApiError
  participant Val as processValidationError
  participant Std as createStandardError

  UI->>Auth: call login/register/forgotPassword/...
  Auth->>Auth: await SDK call
  alt success
    Auth-->>UI: return result
  else error
    Auth->>Proc: processApiError(err, name)
    Proc->>Val: is validation failure?
    alt validation message produced
      Val-->>Proc: friendly message
      Proc-->>Auth: Error(name, message)
    else not validation
      Proc->>Std: createStandardError(originalErr, name)
      Std-->>Proc: Error(name, preserved metadata)
      Proc-->>Auth: Error
    end
    Auth-->>UI: throw Error
  end
Loading
sequenceDiagram
  autonumber
  actor Caller as Caller
  participant SDK as fetchJson
  participant Net as fetch(...)
  participant HFE as handleFetchError
  participant Types as AccountError/extractErrorDetails

  Caller->>SDK: fetchJson(url, opts)
  SDK->>Net: HTTP request
  Net-->>SDK: Response
  alt 204/205/304 or content-length 0
    SDK-->>Caller: { data: undefined, success: true }
  else JSON body
    SDK->>SDK: await response.json() (try/catch)
    SDK-->>Caller: { data: parsedJSON, success: true }
  end
  Note over SDK: on thrown Response or parse error
  SDK->>HFE: handleFetchError(Response)
  HFE->>Types: extractErrorDetails -> AccountError(message,status,details,fields)
  HFE-->>SDK: throw AccountError
  SDK-->>Caller: throw AccountError
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

I nibble code and tidy threads,
Turn raw faults into friendly heads.
Fetches neat and redirects sure,
Validation whispers made more pure.
Hop—this rabbit fixed the cure. 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7c1b084 and 744f5ec.

📒 Files selected for processing (3)
  • libs/portal-framework-auth/src/dataProviders/auth.ts (10 hunks)
  • libs/portal-sdk/src/account.ts (1 hunks)
  • libs/portal-sdk/src/types.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • libs/portal-sdk/src/types.ts
  • libs/portal-sdk/src/account.ts
  • libs/portal-framework-auth/src/dataProviders/auth.ts
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch libs/portal-plugin-dashboard

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
libs/portal-sdk/src/account.ts (1)

380-386: Unlikely branch: e instanceof Response

Native fetch throws TypeError/DOMException on network errors; Response is not typically thrown. Keep if other layers may throw Response, otherwise this can be removed for simplicity.

libs/portal-framework-auth/src/dataProviders/auth.ts (1)

244-245: Switch to processApiError at call sites — nice consolidation

This centralizes formatting and preserves richer metadata via createStandardError. After adopting the validation-preserving tweak above, these sites will also keep fields for forms.

If you want to ensure statusCode is surfaced in notifications/telemetry, you can append it to the error message where appropriate.

Also applies to: 255-256, 295-296, 338-339, 386-387, 415-416, 443-444, 466-467

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5087cb5 and 95b369e.

📒 Files selected for processing (3)
  • libs/portal-framework-auth/src/dataProviders/auth.ts (10 hunks)
  • libs/portal-sdk/src/account.ts (1 hunks)
  • libs/portal-sdk/src/types.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
libs/portal-sdk/src/account.ts (1)
libs/portal-sdk/src/types.ts (1)
  • handleFetchError (94-121)
libs/portal-framework-auth/src/dataProviders/auth.ts (1)
libs/portal-sdk/src/account/generated/accountAPI.schemas.ts (1)
  • Error (74-78)
🔇 Additional comments (4)
libs/portal-sdk/src/types.ts (3)

24-36: AccountError: fields support looks good

Adding fields?: Record<string, string> and wiring it through the ctor is a solid improvement for validation feedback.


40-45: Confirm JSON shape consumers don’t require name

toJSON() now omits name. If any UI/telemetry expects error.name, this could be a breaking change. If needed, include it.

Possible tweak:

   toJSON() {
     return {
+      name: this.name,
       details: this.details,
       fields: this.fields,
       message: this.message,
       statusCode: this.statusCode,
     };
   }

128-132: Pass-through for AccountError is correct

Returning e when it’s already an AccountError prevents double-wrapping.

libs/portal-framework-auth/src/dataProviders/auth.ts (1)

122-122: Validate the exact validation error string

Confirm the backend emits message === "validation failed" (case-sensitive, spacing) so this path always triggers when it should. If not guaranteed, consider a case-insensitive startsWith/equals.

Example:

-const VALIDATION_ERROR_NAME = "validation failed";
+const VALIDATION_ERROR_NAME = "validation failed"; // keep, but match case-insensitively where checked

And in processValidationError:

-  if (error?.message === VALIDATION_ERROR_NAME && error?.fields) {
+  if (typeof error?.message === 'string' && error?.message.toLowerCase() === VALIDATION_ERROR_NAME && error?.fields) {

@pcfreak30 pcfreak30 force-pushed the libs/portal-plugin-dashboard branch from 95b369e to 56557d1 Compare August 31, 2025 17:36
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
libs/portal-sdk/src/types.ts (2)

72-108: Harden extractErrorDetails for RFC7807 and safe stringification

  • Support Problem Details (title/detail, errors) shapes.
  • Wrap JSON.stringify to avoid exceptions on non-serializable inputs.

Apply:

   else if (data?.message) {
     result.message = data.message;
     result.details = data.details;
     result.fields = normalizeFields(data.fields);
-  } else {
-    result.message = JSON.stringify(data);
+  } else if (data?.title || data?.detail) {
+    // RFC 7807 Problem Details
+    result.message = data.title || data.detail;
+    result.details = data;
+    result.fields = normalizeFields(data.errors || data.fields);
+  } else {
+    try {
+      result.message = JSON.stringify(data);
+    } catch {
+      result.message = String(data);
+    }
   }
 
   // Always include fields if they exist at any level
   if (!result.fields) {
-    result.fields = normalizeFields(data?.fields) || normalizeFields(data?.error?.fields);
+    result.fields =
+      normalizeFields(data?.fields) || normalizeFields(data?.error?.fields);
   }

51-67: normalizeFields return type bug (returns undefined but typed non-undefined)

The function returns undefined on Line 52 but the signature says Record<string, string>. This will fail type-checking. Also add a runtime guard for non-objects, filter nulls in arrays, and wrap JSON.stringify to avoid circular refs.

Apply:

-function normalizeFields(fields: Record<string, any>): Record<string, string> {
-  if (!fields) return undefined;
+function normalizeFields(
+  fields: Record<string, any>,
+): Record<string, string> | undefined {
+  if (!fields || typeof fields !== "object") return undefined;
   
   const normalized: Record<string, string> = {};
   for (const [key, value] of Object.entries(fields)) {
     if (Array.isArray(value)) {
-      normalized[key] = value.join(', ');
+      const parts = value.filter((x) => x != null).map(String);
+      normalized[key] = parts.join("; ");
     } else if (value === null || value === undefined) {
       normalized[key] = '';
     } else if (typeof value === 'object') {
-      normalized[key] = JSON.stringify(value);
+      try {
+        normalized[key] = JSON.stringify(value);
+      } catch {
+        normalized[key] = String(value);
+      }
     } else {
       normalized[key] = String(value);
     }
   }
   return normalized;
}
libs/portal-sdk/src/account.ts (1)

390-408: Return typed error on non-empty, non-JSON 2xx responses instead of throwing

Prevents leaking a SyntaxError and losing HTTP context; aligns with earlier guidance.

Apply:

-      // Try to parse JSON, but handle cases where parsing fails due to empty body
-      try {
-        const data = await response.json();
-        return {
-          data: data as T,
-          success: true,
-        };
-      } catch (parseError) {
-        // If JSON parsing fails (e.g., SyntaxError for empty body), treat as no content
-        // Also check for zero content-length header
-        if (this.isResponseEmpty(response)) {
-          return {
-            data: undefined as unknown as T,
-            success: true,
-          };
-        }
-        // Re-throw other errors
-        throw parseError;
-      }
+      // Try to parse JSON, but handle cases where parsing fails (empty body or non-JSON)
+      const clone = response.clone();
+      try {
+        const data = await response.json();
+        return {
+          data: data as T,
+          success: true,
+        };
+      } catch {
+        // Treat empty body as no content
+        if (this.isResponseEmpty(response)) {
+          return {
+            data: undefined as unknown as T,
+            success: true,
+          };
+        }
+        const txt = await clone.text().catch(() => '');
+        if (!txt || txt.trim().length === 0) {
+          return {
+            data: undefined as unknown as T,
+            success: true,
+          };
+        }
+        // Non-empty non-JSON success payloads are unexpected; surface a typed error
+        return {
+          error: new AccountError('Invalid JSON response', response.status),
+          success: false,
+        };
+      }
🧹 Nitpick comments (4)
libs/portal-sdk/src/types.ts (2)

119-149: Broaden JSON detection and preserve cause in fallback

  • Consider application/problem+json by checking for “json” substring.
  • Include the caught exception as details on the last-resort path for easier debugging.

Apply:

-    const contentType = response.headers.get('content-type');
+    const contentType = response.headers.get('content-type');
+    const isJson = contentType?.toLowerCase()?.includes('json');
@@
-    if (contentType?.includes('application/json')) {
+    if (isJson) {
       try {
         errorData = await response.json();
       } catch {
         // Preserve status; fall back to text/statusText
         const txt = await clone.text().catch(() => '');
         errorData = txt || response.statusText;
       }
@@
-  } catch (e) {
+  } catch (e) {
     // As a last resort, still preserve the HTTP status when possible
-    return new AccountError(response.statusText || 'Unknown error', response.status);
+    return new AccountError(
+      response.statusText || 'Unknown error',
+      response.status,
+      { cause: e }
+    );
   }

158-171: Preserve original error as cause and stringify unknowns safely

Keep stack/context by passing the original error/object in details; guard JSON.stringify failures.

Apply:

   if (e instanceof Error) {
-    return new AccountError(e.message, 500);
+    return new AccountError(e.message, 500, { cause: e });
   }
 
   if (typeof e === "object" && e !== null) {
-    return new AccountError(JSON.stringify(e), 500);
+    let msg: string;
+    try {
+      msg = JSON.stringify(e);
+    } catch {
+      msg = String(e);
+    }
+    return new AccountError(msg, 500, { cause: e });
   }
libs/portal-sdk/src/account.ts (2)

340-355: Consider more no-body statuses (205, 304) in isResponseEmpty

Extend the status check to include 205 Reset Content and 304 Not Modified, which typically lack bodies.

Apply:

-    if (response.status === 204) {
+    if (response.status === 204 || response.status === 205 || response.status === 304) {
       return true;
     }

410-415: Remove unreachable branch (e instanceof Response) in catch

fetch throws TypeError on network errors; this function never throws a Response. This branch adds noise.

Apply:

-      if (e instanceof Response) {
-        return {
-          error: await handleFetchError(e),
-          success: false,
-        };
-      }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 95b369e and 56557d1.

📒 Files selected for processing (3)
  • libs/portal-framework-auth/src/dataProviders/auth.ts (10 hunks)
  • libs/portal-sdk/src/account.ts (2 hunks)
  • libs/portal-sdk/src/types.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/portal-framework-auth/src/dataProviders/auth.ts
🧰 Additional context used
🧬 Code graph analysis (1)
libs/portal-sdk/src/account.ts (1)
libs/portal-sdk/src/types.ts (1)
  • handleFetchError (115-150)
🔇 Additional comments (1)
libs/portal-sdk/src/types.ts (1)

24-36: AccountError: fields support looks good; verify downstream JSON shape change

You added fields and updated toJSON to omit name. Confirm no consumers rely on name in serialized errors.

@pcfreak30 pcfreak30 force-pushed the libs/portal-plugin-dashboard branch from 56557d1 to 7c1b084 Compare August 31, 2025 17:50
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
libs/portal-sdk/src/account.ts (1)

373-391: Guard JSON parsing and surface typed error for non-JSON 2xx

If a 2xx success returns non-JSON (non-empty) content, response.json() throws; current code rethrows, then the outer catch misclassifies it. Clone the response to safely inspect body; treat empty as no-content, otherwise return a typed AccountError("Invalid JSON response").

-      // Try to parse JSON, but handle cases where parsing fails due to empty body
-      try {
-        const data = await response.json();
-        return {
-          data: data as T,
-          success: true,
-        };
-      } catch (parseError) {
-        // If JSON parsing fails (e.g., SyntaxError for empty body), treat as no content
-        // Also check for zero content-length header
-        if (this.isResponseEmpty(response)) {
-          return {
-            data: undefined as unknown as T,
-            success: true,
-          };
-        }
-        // Re-throw other errors
-        throw parseError;
-      }
+      // Try to parse JSON, but handle cases where parsing fails due to empty body
+      const clone = response.clone();
+      try {
+        const data = await response.json();
+        return {
+          data: data as T,
+          success: true,
+        };
+      } catch {
+        // If JSON parsing fails, treat empty body as no content; otherwise surface a typed error
+        const txt = await clone.text().catch(() => "");
+        if (!txt || this.isResponseEmpty(response)) {
+          return {
+            data: undefined as unknown as T,
+            success: true,
+          };
+        }
+        return {
+          error: new AccountError("Invalid JSON response", response.status),
+          success: false,
+        };
+      }
🧹 Nitpick comments (2)
libs/portal-framework-auth/src/dataProviders/auth.ts (2)

67-85: Broaden validation-error detection beyond message

Backends often set the sentinel on error (or nested details.error) rather than message. Match those too to reliably trigger field-message extraction.

-const processValidationError = (error: any): string | undefined => {
-  if (error?.message === VALIDATION_ERROR_NAME && error?.fields) {
+const processValidationError = (error: any): string | undefined => {
+  const isValidation =
+    error?.message === VALIDATION_ERROR_NAME ||
+    error?.error === VALIDATION_ERROR_NAME ||
+    error?.details?.error === VALIDATION_ERROR_NAME;
+  if (isValidation && error?.fields) {

88-99: Preserve specialized error instances (e.g., AccountError)

Re-wrapping an existing AccountError loses instanceof semantics. If the original is a subclass, set name and return it; only create a new Error for non-Error inputs or plain Error.

 const createStandardError = (error: unknown, name: string): Error => {
   const original = error instanceof Error ? error : new Error(String(error));
-  const e = new Error(original.message);
-  e.name = name;
-  e.stack = original.stack;
-  if ((original as any).cause) (e as any).cause = (original as any).cause;
-  Object.keys(original).forEach((key) => {
-    if (!(key in e)) (e as any)[key] = (original as any)[key];
-  });
-  return e;
+  // Preserve prototype/data for subclasses like AccountError
+  if (original.constructor !== Error) {
+    original.name = name;
+    return original;
+  }
+  const wrapped = new Error(original.message);
+  wrapped.name = name;
+  wrapped.stack = original.stack;
+  if ((original as any).cause) (wrapped as any).cause = (original as any).cause;
+  Object.keys(original).forEach((key) => {
+    if (!(key in wrapped)) (wrapped as any)[key] = (original as any)[key];
+  });
+  return wrapped;
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 56557d1 and 7c1b084.

📒 Files selected for processing (3)
  • libs/portal-framework-auth/src/dataProviders/auth.ts (10 hunks)
  • libs/portal-sdk/src/account.ts (1 hunks)
  • libs/portal-sdk/src/types.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/portal-sdk/src/types.ts
🧰 Additional context used
🧬 Code graph analysis (2)
libs/portal-sdk/src/account.ts (1)
libs/portal-sdk/src/types.ts (1)
  • handleFetchError (115-155)
libs/portal-framework-auth/src/dataProviders/auth.ts (1)
libs/portal-sdk/src/account/generated/accountAPI.schemas.ts (1)
  • Error (74-78)
🔇 Additional comments (4)
libs/portal-sdk/src/account.ts (2)

365-371: Empty-response handling looks good

Covers 204/205/304 and zero content-length via isResponseEmpty. Nice.


400-422: Helper isResponseEmpty is solid

Reasonable status checks plus strict Content-Length parsing. This centralizes a previously error-prone pattern.

libs/portal-framework-auth/src/dataProviders/auth.ts (2)

100-113: processApiError flow reads well

Prefers validation message when present; otherwise standardizes. Good balance for UX while preserving metadata via createStandardError.


242-242: Sanity check passed: no wrapErrorWithName( in source code
The only matches are in build artifacts under go/portal-plugin-dashboard/build/static/js, so all source references have been replaced by processApiError.

…nd processing for auth operations

- Replace `wrapErrorWithName` with `processApiError` and `createStandardError` for more robust error handling
- Add `processValidationError` helper to parse and clean validation error messages
- Update `AccountError` class to include `fields` property for detailed validation errors
- Enhance `handleFetchError` to extract error details and fields from various response formats
- Handle unknown errors by returning existing `AccountError` instances unchanged
@pcfreak30 pcfreak30 force-pushed the libs/portal-plugin-dashboard branch from 7c1b084 to 744f5ec Compare August 31, 2025 17:58
@pcfreak30 pcfreak30 merged commit 89d48d7 into develop Aug 31, 2025
1 of 2 checks passed
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