From 14ecae61dce0cd136425636484ca81c4c313be9a Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Fri, 3 Apr 2026 15:10:11 +0200
Subject: [PATCH] feat: enhance error handling in Stalwart API responses
---
app/api/account/stalwart/auth/route.ts | 19 +++++++++++++++++--
app/api/account/stalwart/crypto/route.ts | 19 +++++++++++++++++--
app/api/account/stalwart/principal/route.ts | 19 +++++++++++++++++--
.../settings/account-security-settings.tsx | 3 ++-
locales/de/common.json | 3 ++-
locales/en/common.json | 3 ++-
locales/es/common.json | 3 ++-
locales/fr/common.json | 3 ++-
locales/it/common.json | 3 ++-
locales/ja/common.json | 3 ++-
locales/nl/common.json | 3 ++-
locales/pt/common.json | 3 ++-
locales/ru/common.json | 3 ++-
13 files changed, 71 insertions(+), 16 deletions(-)
diff --git a/app/api/account/stalwart/auth/route.ts b/app/api/account/stalwart/auth/route.ts
index d1ee34a3..4fa0f4d0 100644
--- a/app/api/account/stalwart/auth/route.ts
+++ b/app/api/account/stalwart/auth/route.ts
@@ -2,6 +2,20 @@ import { NextRequest, NextResponse } from 'next/server';
import { logger } from '@/lib/logger';
import { getStalwartCredentials } from '@/lib/stalwart/credentials';
+/**
+ * Parse Stalwart error response to extract meaningful error message
+ */
+function parseStalwartError(responseText: string): string {
+ try {
+ const error = JSON.parse(responseText);
+ if (error.detail) return error.detail;
+ if (error.error) return error.error;
+ return `HTTP ${error.status || 'Error'}`;
+ } catch {
+ return responseText;
+ }
+}
+
/**
* GET /api/account/stalwart/auth
* Proxy to Stalwart GET /api/account/auth
@@ -20,9 +34,10 @@ export async function GET(request: NextRequest) {
if (!response.ok) {
const text = await response.text();
- logger.warn('Stalwart auth info failed', { status: response.status });
+ const detail = parseStalwartError(text);
+ logger.warn('Stalwart auth info failed', { status: response.status, detail });
return NextResponse.json(
- { error: 'Failed to fetch auth info', details: text },
+ { error: detail || 'Failed to fetch auth info' },
{ status: response.status }
);
}
diff --git a/app/api/account/stalwart/crypto/route.ts b/app/api/account/stalwart/crypto/route.ts
index 6c5f3502..07681115 100644
--- a/app/api/account/stalwart/crypto/route.ts
+++ b/app/api/account/stalwart/crypto/route.ts
@@ -2,6 +2,20 @@ import { NextRequest, NextResponse } from 'next/server';
import { logger } from '@/lib/logger';
import { getStalwartCredentials } from '@/lib/stalwart/credentials';
+/**
+ * Parse Stalwart error response to extract meaningful error message
+ */
+function parseStalwartError(responseText: string): string {
+ try {
+ const error = JSON.parse(responseText);
+ if (error.detail) return error.detail;
+ if (error.error) return error.error;
+ return `HTTP ${error.status || 'Error'}`;
+ } catch {
+ return responseText;
+ }
+}
+
/**
* GET /api/account/stalwart/crypto
* Proxy to Stalwart GET /api/account/crypto
@@ -20,9 +34,10 @@ export async function GET(request: NextRequest) {
if (!response.ok) {
const text = await response.text();
- logger.warn('Stalwart crypto info failed', { status: response.status });
+ const detail = parseStalwartError(text);
+ logger.warn('Stalwart crypto info failed', { status: response.status, detail });
return NextResponse.json(
- { error: 'Failed to fetch crypto info', details: text },
+ { error: detail || 'Failed to fetch crypto info' },
{ status: response.status }
);
}
diff --git a/app/api/account/stalwart/principal/route.ts b/app/api/account/stalwart/principal/route.ts
index 5b7131cb..8801c2b6 100644
--- a/app/api/account/stalwart/principal/route.ts
+++ b/app/api/account/stalwart/principal/route.ts
@@ -2,6 +2,20 @@ import { NextRequest, NextResponse } from 'next/server';
import { logger } from '@/lib/logger';
import { getStalwartCredentials } from '@/lib/stalwart/credentials';
+/**
+ * Parse Stalwart error response to extract meaningful error message
+ */
+function parseStalwartError(responseText: string): string {
+ try {
+ const error = JSON.parse(responseText);
+ if (error.detail) return error.detail;
+ if (error.error) return error.error;
+ return `HTTP ${error.status || 'Error'}`;
+ } catch {
+ return responseText;
+ }
+}
+
/**
* GET /api/account/stalwart/principal
* Proxy to Stalwart GET /api/principal/{username}
@@ -20,9 +34,10 @@ export async function GET(request: NextRequest) {
if (!response.ok) {
const text = await response.text();
- logger.warn('Stalwart principal fetch failed', { status: response.status });
+ const detail = parseStalwartError(text);
+ logger.warn('Stalwart principal fetch failed', { status: response.status, detail });
return NextResponse.json(
- { error: 'Failed to fetch principal', details: text },
+ { error: detail || 'Failed to fetch principal' },
{ status: response.status }
);
}
diff --git a/components/settings/account-security-settings.tsx b/components/settings/account-security-settings.tsx
index 51b3a972..5f1624d4 100644
--- a/components/settings/account-security-settings.tsx
+++ b/components/settings/account-security-settings.tsx
@@ -157,6 +157,7 @@ function DisplayNameSection() {
setName(e.target.value)}
+ placeholder={displayName || t('display_name.placeholder')}
className="w-48"
/>