Skip to content

fix(browser-extension): API login error when server address includes /api/v1 suffix#2630

Open
xingzihai wants to merge 6 commits intokarakeep-app:mainfrom
xingzihai:fix/api-login-clean
Open

fix(browser-extension): API login error when server address includes /api/v1 suffix#2630
xingzihai wants to merge 6 commits intokarakeep-app:mainfrom
xingzihai:fix/api-login-clean

Conversation

@xingzihai
Copy link
Copy Markdown
Contributor

Fixes #2622

Summary

Fix API login error when server address includes /api/v1 suffix.

Changes

  • Add normalizeServerAddress() to automatically strip API path suffixes
  • Add customFetch() for better error handling
  • Add URL validation for security

Testing

  • Tested with various server address formats
  • Verified error messages are user-friendly

Fixes karakeep-app#2622 - Karakeep API login error

Users were experiencing "Unexpected non-whitespace character after JSON at position 4"
errors when entering server addresses with /api/v1 or /api suffixes.

Changes:
- Add normalizeServerAddress() to strip common API path suffixes from server address
- Add customFetch() to detect non-JSON responses and provide better error messages
- Add getErrorMessage() to convert tRPC errors to user-friendly messages
- Show warning when server address is automatically corrected
- Add example hint for server address input field

Root cause: Users entering server address as "http://localhost:4000/api/v1"
instead of just "http://localhost:4000", causing the tRPC client to call
non-existent endpoints and receive HTML error pages instead of JSON.
…ngs schema

- Add URL scheme and format validation to normalizeServerAddress()
- Add HTTP/HTTPS URL validation to zSettingsSchema for address field
- Throws error for invalid URLs to prevent security bypass
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88af7369-f0ef-4431-892e-ec9a53623973

📥 Commits

Reviewing files that changed from the base of the PR and between d3a0b9a and c579f09.

📒 Files selected for processing (1)
  • apps/browser-extension/src/utils/settings.ts

Walkthrough

Add server-address normalization/validation and a user-warning flow in the NotConfigured page; introduce URL normalization util; add a customFetch and error-formatting helpers for tRPC; adjust settings schema to normalize stored address; update pages to use the new error formatting.

Changes

Cohort / File(s) Summary
NotConfigured / UI
apps/browser-extension/src/NotConfiguredPage.tsx
Added warning state and example text; onSave now calls normalizeServerAddress(input) (errors set error); when normalization changes input, update serverAddress, set warning, and abort navigation; clear warning on edits.
Error helpers & fetch
apps/browser-extension/src/utils/trpc.ts
Added customFetch to httpBatchLink to inspect non-JSON responses and throw clearer errors; exported SERVER_ADDRESS_ERROR_MESSAGE, isJsonParseError, and getErrorMessage for classifying/formatting errors.
URL normalization
apps/browser-extension/src/utils/url.ts
Added normalizeServerAddress(address: string) to trim, remove trailing slashes and /api or /api/v{n} suffixes, enforce http/https scheme, validate via new URL(...), and throw distinct errors for invalid scheme/URL.
Settings schema
apps/browser-extension/src/utils/settings.ts
Changed zSettingsSchema.address to validate/transform address: on invalid/missing input return default (https://cloud.karakeep.app); when valid, store only the origin (protocol + host + port).
Pages using error formatting
apps/browser-extension/src/OptionsPage.tsx, apps/browser-extension/src/SignInPage.tsx
Replaced direct .message usage with getErrorMessage(...) to obtain user-visible error text from TRPC/fetch errors.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main fix: handling API login errors when server addresses include /api/v1 suffix, which aligns with the primary change.
Description check ✅ Passed The description is related to the changeset, referencing issue #2622 and outlining the changes made to normalize server addresses and handle errors.
Linked Issues check ✅ Passed The PR implements all primary objectives from #2622: normalizes server addresses by stripping /api/v1 suffixes, improves error handling with customFetch, adds URL validation, and provides user-friendly error messages.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue objectives: normalizeServerAddress() and URL validation address the core issue, customFetch() improves error handling, and UI updates provide user guidance.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 27, 2026

Greptile Summary

This PR fixes the browser extension login failure that occurred when users entered a server address with a /api/v1 suffix by adding address normalization, a custom fetch wrapper for non-JSON error detection, and improved error messages throughout the sign-in flow.

Key changes and issues found:

  • NotConfiguredPage.tsx: The new warning message (shown when an address is auto-corrected) is dead code — navigate("/signin") fires on the same tick, navigating the user away before React renders the warning. Additionally, the call to normalizeServerAddress() is not wrapped in a try/catch, so malformed inputs like http:// (which pass the isHttpUrl guard but fail new URL()) would throw an unhandled exception.
  • utils/settings.ts: The new isHttpUrl refine on the address Zod field could cause the entire settings parse to fail for users who happen to have a non-HTTP address stored from a previous edge case, silently resetting all settings (including apiKey) to defaults.
  • utils/trpc.ts: customFetch correctly intercepts HTML error pages before tRPC tries to parse them. However, errors thrown by customFetch already contain SERVER_ADDRESS_ERROR_MESSAGE embedded in their message, so getErrorMessage will return the message twice (the custom prefix plus the constant).
  • SignInPage.tsx / OptionsPage.tsx: The migration to getErrorMessage() is clean and correct.

Confidence Score: 4/5

Safe to merge after fixing the dead warning (never shown) and the uncaught exception in onSave.

Two P1 issues exist in NotConfiguredPage.tsx: the warning is immediately overwritten by navigation and never seen by the user, and normalizeServerAddress() can throw uncaught for certain valid-looking inputs. These are functional bugs in the core user flow this PR targets.

apps/browser-extension/src/NotConfiguredPage.tsx requires the most attention (warning visibility + uncaught throw). apps/browser-extension/src/utils/settings.ts warrants a second look for the Zod refine side-effects on stored data.

Important Files Changed

Filename Overview
apps/browser-extension/src/NotConfiguredPage.tsx Adds address normalization and a warning UI, but the warning is never shown (navigation fires immediately) and the normalizeServerAddress call is uncaught.
apps/browser-extension/src/utils/url.ts Adds normalizeServerAddress() that strips /api/v1 and /api suffixes and validates the URL; logic is sound but can throw for inputs like "http://" that pass isHttpUrl yet fail new URL().
apps/browser-extension/src/utils/trpc.ts Adds customFetch for non-JSON detection and getErrorMessage for user-friendly errors; customFetch works correctly, but the doubled error message path in getErrorMessage is minor.
apps/browser-extension/src/utils/settings.ts Adds isHttpUrl refine to the address Zod schema; could silently reset all settings (including apiKey) for users with edge-case stored addresses that no longer pass validation.
apps/browser-extension/src/SignInPage.tsx Replaces loginError.message with getErrorMessage(loginError) for better error display; straightforward and correct.
apps/browser-extension/src/OptionsPage.tsx Replaces raw whoAmIError.message with getErrorMessage(); minimal and correct change.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/browser-extension/src/NotConfiguredPage.tsx
Line: 41-53

Comment:
**Warning is never visible to the user**

`setWarning(...)` is called but `navigate("/signin")` runs unconditionally on the very next line. The component unmounts before React can render the warning message, so the user never sees it. The correction is silently applied without any feedback.

To fix this, the navigation should be deferred when an address correction occurs (e.g., only navigate after the user acknowledges the warning by pressing "Configure" a second time), or the warning approach should be dropped in favour of simply updating the input field value to the normalized address so the user can review it before confirming:

```suggestion
    // Normalize the address by stripping /api/v1 or /api suffixes
    const normalizedAddress = normalizeServerAddress(input);

    // If the address was normalized, update the input and show a warning
    // instead of navigating away, so the user can see the correction.
    if (normalizedAddress !== input) {
      setWarning(
        `Address was automatically corrected from "${input}" to "${normalizedAddress}". ` +
          `The server address should be the base URL without /api/v1 or /api suffix.`,
      );
      setServerAddress(normalizedAddress);
      return;
    }

    setSettings((s) => ({ ...s, address: normalizedAddress }));
    navigate("/signin");
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/browser-extension/src/NotConfiguredPage.tsx
Line: 42

Comment:
**Uncaught exception from `normalizeServerAddress`**

`normalizeServerAddress` can throw if `new URL(normalized)` fails — for example when the user types `http://` (empty host). This input passes the `isHttpUrl` guard above (it starts with `"http://"`), but `new URL("http://")` throws a `TypeError: Invalid URL` that propagates unhandled from `onSave`, crashing the handler silently.

Wrap the call in a `try/catch` and surface the error to the user:

```suggestion
    let normalizedAddress: string;
    try {
      normalizedAddress = normalizeServerAddress(input);
    } catch (e) {
      setError(e instanceof Error ? e.message : "Invalid server address");
      return;
    }
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/browser-extension/src/utils/trpc.ts
Line: 53-56

Comment:
**`customFetch` doubles the error message for HTML responses**

When `customFetch` throws `"Server returned an HTML error page instead of JSON. <SERVER_ADDRESS_ERROR_MESSAGE>"`, the error propagates as a `TRPCClientError`. In `getErrorMessage`, `isJsonParseError(error.cause)` returns `false` (the cause message contains "Server returned…" not "unexpected…json/position"), so the function falls through to `return error.message`, which includes the full duplicated sentence.

The user would see: `"Server returned an HTML error page instead of JSON. Unable to connect to the Karakeep server…"` — information that is stated twice in different ways.

Consider either:
- Having `customFetch` throw with only `SERVER_ADDRESS_ERROR_MESSAGE`, or
- Having `getErrorMessage` check whether `error.message` already contains the well-known server error message before returning it.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/browser-extension/src/utils/settings.ts
Line: 12-19

Comment:
**Zod `refine` may silently reset settings for edge-case stored values**

The new `.refine(isHttpUrl, …)` is added to the `address` field. If any user has an address stored in Chrome sync storage that doesn't pass `isHttpUrl` (e.g., an empty string from a previous bug, or a value written outside the normal UI flow), `zSettingsSchema.safeParse` will now fail for those users.

Looking at `getPluginSettings`, a `safeParse` failure returns `DEFAULT_SETTINGS` (which has `apiKey: ""`), effectively logging those users out and resetting all their settings. Given the extension previously accepted _any_ string for `address`, this is a silent breaking change for an existing persisted state edge case.

A safer approach is to keep the schema permissive for storage (since validation and normalization are already enforced on input) and only apply `refine` at the point of user input, or to apply `.catch("https://cloud.karakeep.app")` on the field so an invalid value falls back only for that field rather than invalidating the whole settings object.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "style: add trailing newlines to modified..." | Re-trigger Greptile

Comment on lines 41 to 53
// Normalize the address by stripping /api/v1 or /api suffixes
const normalizedAddress = normalizeServerAddress(input);

// Show a warning if the address was normalized
if (normalizedAddress !== input) {
setWarning(
`Address was automatically corrected from "${input}" to "${normalizedAddress}". ` +
`The server address should be the base URL without /api/v1 or /api suffix.`,
);
}

setSettings((s) => ({ ...s, address: normalizedAddress }));
navigate("/signin");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Warning is never visible to the user

setWarning(...) is called but navigate("/signin") runs unconditionally on the very next line. The component unmounts before React can render the warning message, so the user never sees it. The correction is silently applied without any feedback.

To fix this, the navigation should be deferred when an address correction occurs (e.g., only navigate after the user acknowledges the warning by pressing "Configure" a second time), or the warning approach should be dropped in favour of simply updating the input field value to the normalized address so the user can review it before confirming:

Suggested change
// Normalize the address by stripping /api/v1 or /api suffixes
const normalizedAddress = normalizeServerAddress(input);
// Show a warning if the address was normalized
if (normalizedAddress !== input) {
setWarning(
`Address was automatically corrected from "${input}" to "${normalizedAddress}". ` +
`The server address should be the base URL without /api/v1 or /api suffix.`,
);
}
setSettings((s) => ({ ...s, address: normalizedAddress }));
navigate("/signin");
// Normalize the address by stripping /api/v1 or /api suffixes
const normalizedAddress = normalizeServerAddress(input);
// If the address was normalized, update the input and show a warning
// instead of navigating away, so the user can see the correction.
if (normalizedAddress !== input) {
setWarning(
`Address was automatically corrected from "${input}" to "${normalizedAddress}". ` +
`The server address should be the base URL without /api/v1 or /api suffix.`,
);
setServerAddress(normalizedAddress);
return;
}
setSettings((s) => ({ ...s, address: normalizedAddress }));
navigate("/signin");
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/browser-extension/src/NotConfiguredPage.tsx
Line: 41-53

Comment:
**Warning is never visible to the user**

`setWarning(...)` is called but `navigate("/signin")` runs unconditionally on the very next line. The component unmounts before React can render the warning message, so the user never sees it. The correction is silently applied without any feedback.

To fix this, the navigation should be deferred when an address correction occurs (e.g., only navigate after the user acknowledges the warning by pressing "Configure" a second time), or the warning approach should be dropped in favour of simply updating the input field value to the normalized address so the user can review it before confirming:

```suggestion
    // Normalize the address by stripping /api/v1 or /api suffixes
    const normalizedAddress = normalizeServerAddress(input);

    // If the address was normalized, update the input and show a warning
    // instead of navigating away, so the user can see the correction.
    if (normalizedAddress !== input) {
      setWarning(
        `Address was automatically corrected from "${input}" to "${normalizedAddress}". ` +
          `The server address should be the base URL without /api/v1 or /api suffix.`,
      );
      setServerAddress(normalizedAddress);
      return;
    }

    setSettings((s) => ({ ...s, address: normalizedAddress }));
    navigate("/signin");
```

How can I resolve this? If you propose a fix, please make it concise.


setSettings((s) => ({ ...s, address: input.replace(/\/$/, "") }));
// Normalize the address by stripping /api/v1 or /api suffixes
const normalizedAddress = normalizeServerAddress(input);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Uncaught exception from normalizeServerAddress

normalizeServerAddress can throw if new URL(normalized) fails — for example when the user types http:// (empty host). This input passes the isHttpUrl guard above (it starts with "http://"), but new URL("http://") throws a TypeError: Invalid URL that propagates unhandled from onSave, crashing the handler silently.

Wrap the call in a try/catch and surface the error to the user:

Suggested change
const normalizedAddress = normalizeServerAddress(input);
let normalizedAddress: string;
try {
normalizedAddress = normalizeServerAddress(input);
} catch (e) {
setError(e instanceof Error ? e.message : "Invalid server address");
return;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/browser-extension/src/NotConfiguredPage.tsx
Line: 42

Comment:
**Uncaught exception from `normalizeServerAddress`**

`normalizeServerAddress` can throw if `new URL(normalized)` fails — for example when the user types `http://` (empty host). This input passes the `isHttpUrl` guard above (it starts with `"http://"`), but `new URL("http://")` throws a `TypeError: Invalid URL` that propagates unhandled from `onSave`, crashing the handler silently.

Wrap the call in a `try/catch` and surface the error to the user:

```suggestion
    let normalizedAddress: string;
    try {
      normalizedAddress = normalizeServerAddress(input);
    } catch (e) {
      setError(e instanceof Error ? e.message : "Invalid server address");
      return;
    }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +53 to +56
throw new Error(
`Server returned an unexpected response: "${preview}...". ${SERVER_ADDRESS_ERROR_MESSAGE}`,
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 customFetch doubles the error message for HTML responses

When customFetch throws "Server returned an HTML error page instead of JSON. <SERVER_ADDRESS_ERROR_MESSAGE>", the error propagates as a TRPCClientError. In getErrorMessage, isJsonParseError(error.cause) returns false (the cause message contains "Server returned…" not "unexpected…json/position"), so the function falls through to return error.message, which includes the full duplicated sentence.

The user would see: "Server returned an HTML error page instead of JSON. Unable to connect to the Karakeep server…" — information that is stated twice in different ways.

Consider either:

  • Having customFetch throw with only SERVER_ADDRESS_ERROR_MESSAGE, or
  • Having getErrorMessage check whether error.message already contains the well-known server error message before returning it.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/browser-extension/src/utils/trpc.ts
Line: 53-56

Comment:
**`customFetch` doubles the error message for HTML responses**

When `customFetch` throws `"Server returned an HTML error page instead of JSON. <SERVER_ADDRESS_ERROR_MESSAGE>"`, the error propagates as a `TRPCClientError`. In `getErrorMessage`, `isJsonParseError(error.cause)` returns `false` (the cause message contains "Server returned…" not "unexpected…json/position"), so the function falls through to `return error.message`, which includes the full duplicated sentence.

The user would see: `"Server returned an HTML error page instead of JSON. Unable to connect to the Karakeep server…"` — information that is stated twice in different ways.

Consider either:
- Having `customFetch` throw with only `SERVER_ADDRESS_ERROR_MESSAGE`, or
- Having `getErrorMessage` check whether `error.message` already contains the well-known server error message before returning it.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +12 to +19
address: z
.string()
.refine(
(val) => isHttpUrl(val),
"Server address must be a valid HTTP or HTTPS URL",
)
.optional()
.default("https://cloud.karakeep.app"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Zod refine may silently reset settings for edge-case stored values

The new .refine(isHttpUrl, …) is added to the address field. If any user has an address stored in Chrome sync storage that doesn't pass isHttpUrl (e.g., an empty string from a previous bug, or a value written outside the normal UI flow), zSettingsSchema.safeParse will now fail for those users.

Looking at getPluginSettings, a safeParse failure returns DEFAULT_SETTINGS (which has apiKey: ""), effectively logging those users out and resetting all their settings. Given the extension previously accepted any string for address, this is a silent breaking change for an existing persisted state edge case.

A safer approach is to keep the schema permissive for storage (since validation and normalization are already enforced on input) and only apply refine at the point of user input, or to apply .catch("https://cloud.karakeep.app") on the field so an invalid value falls back only for that field rather than invalidating the whole settings object.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/browser-extension/src/utils/settings.ts
Line: 12-19

Comment:
**Zod `refine` may silently reset settings for edge-case stored values**

The new `.refine(isHttpUrl, …)` is added to the `address` field. If any user has an address stored in Chrome sync storage that doesn't pass `isHttpUrl` (e.g., an empty string from a previous bug, or a value written outside the normal UI flow), `zSettingsSchema.safeParse` will now fail for those users.

Looking at `getPluginSettings`, a `safeParse` failure returns `DEFAULT_SETTINGS` (which has `apiKey: ""`), effectively logging those users out and resetting all their settings. Given the extension previously accepted _any_ string for `address`, this is a silent breaking change for an existing persisted state edge case.

A safer approach is to keep the schema permissive for storage (since validation and normalization are already enforced on input) and only apply `refine` at the point of user input, or to apply `.catch("https://cloud.karakeep.app")` on the field so an invalid value falls back only for that field rather than invalidating the whole settings object.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@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: 5

🧹 Nitpick comments (2)
apps/browser-extension/src/utils/trpc.ts (1)

53-55: Avoid exposing raw response previews in user-facing errors.

Line 54 includes server response snippets directly in the error message. Prefer a sanitized message for UI and keep raw payload details only for internal/debug logging.

Proposed fix
-      throw new Error(
-        `Server returned an unexpected response: "${preview}...". ${SERVER_ADDRESS_ERROR_MESSAGE}`,
-      );
+      throw new Error(
+        `Server returned an unexpected non-JSON response. ${SERVER_ADDRESS_ERROR_MESSAGE}`,
+      );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/browser-extension/src/utils/trpc.ts` around lines 53 - 55, The thrown
Error in utils/trpc.ts embeds the raw `preview` response into a user-facing
message; remove the raw snippet from the Error text and replace it with a
sanitized, generic message (e.g., "Server returned an unexpected response.")
combined with the existing `SERVER_ADDRESS_ERROR_MESSAGE`; meanwhile send the
raw `preview` to an internal debug log (e.g., `console.debug` or your app
logger) before throwing so developers can inspect it without exposing it to
users. Target the throw site that references `preview` and
`SERVER_ADDRESS_ERROR_MESSAGE` and ensure no other user-facing errors include
un-sanitized payloads.
apps/browser-extension/src/NotConfiguredPage.tsx (1)

30-30: Prefer strict equality === over loose equality ==.

TypeScript best practice is to use strict equality comparison.

✨ Suggested change
-   if (input == "") {
+   if (input === "") {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/browser-extension/src/NotConfiguredPage.tsx` at line 30, In
NotConfiguredPage.tsx update the loose equality check in the conditional that
reads "if (input == "")" to use strict equality instead; locate the conditional
inside the NotConfiguredPage component (the input validation branch) and change
the comparison operator from == to === so the check becomes a strict comparison
against the empty string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/browser-extension/src/NotConfiguredPage.tsx`:
- Around line 41-53: The current flow normalizes the server address
(normalizeServerAddress/input -> normalizedAddress), sets a warning via
setWarning, then immediately calls navigate("/signin"), which prevents the user
from seeing the warning; change the behavior so navigation is not automatic when
an address normalization occurs: when normalizedAddress !== input, setWarning
and update settings (setSettings) but DO NOT call navigate("/signin"); instead
require explicit user confirmation (e.g., a "Continue" button in the UI that
calls navigate("/signin") when clicked) so the user can read the warning before
proceeding; keep automatic navigation only when no normalization occurred.
- Line 42: normalizeServerAddress(input) can throw even when isHttpUrl(input)
previously passed; wrap the call to normalizeServerAddress inside a try-catch in
NotConfiguredPage.tsx (where normalizedAddress is assigned) to catch and handle
errors. On catch, display a user-friendly validation error or set the component
state to mark the input invalid (same flow as isHttpUrl failure), and avoid
letting the exception bubble; ensure you reference normalizeServerAddress and
isHttpUrl so the input validation path consistently handles invalid URL formats.

In `@apps/browser-extension/src/utils/settings.ts`:
- Around line 12-19: The address field's new .refine(isHttpUrl) causes safeParse
failures and silent fallback to DEFAULT_SETTINGS in getPluginSettings() and
subscribeToSettingsChanges(); update settings.ts to handle legacy bare-host
addresses by (1) attempting a migration: if the stored address fails isHttpUrl,
try prepending "https://" and then "http://" and accept the first valid URL, (2)
if migration fails, log a clear error via the same logger used in
getPluginSettings()/subscribeToSettingsChanges() and preserve the original
stored value (or surface a user-visible warning) instead of silently replacing
it with DEFAULT_SETTINGS, and (3) ensure the schema validation still uses the
refined check but that these migration/logging steps run before deciding to
fallback so users with self-hosted hosts like "localhost:5000" or "example.com"
are handled gracefully.

In `@apps/browser-extension/src/utils/trpc.ts`:
- Around line 33-37: The content-type header check uses a case-sensitive
substring test and can miss JSON when header casing differs; normalize the
header once by taking the contentType string (the local variable `contentType`)
and converting it to lower-case (e.g., `contentType = contentType.toLowerCase()`
or use a lower-cased copy) before testing `.includes("application/json")` in the
`response` handling block so the condition `!response.ok && (!contentType ||
!contentType.includes("application/json"))` correctly detects JSON responses
regardless of header casing.

In `@apps/browser-extension/src/utils/url.ts`:
- Around line 37-43: The scheme check is case-sensitive and can disagree with
isHttpUrl(); fix by performing the same case-insensitive check used in
isHttpUrl(): convert the URL string (the variable normalized) to lowercase
before testing startsWith("http://") / startsWith("https://"), or reuse/call
isHttpUrl(normalized) here instead of manual startsWith checks so inputs like
"HTTP://localhost:3000" won't erroneously throw in this block.

---

Nitpick comments:
In `@apps/browser-extension/src/NotConfiguredPage.tsx`:
- Line 30: In NotConfiguredPage.tsx update the loose equality check in the
conditional that reads "if (input == "")" to use strict equality instead; locate
the conditional inside the NotConfiguredPage component (the input validation
branch) and change the comparison operator from == to === so the check becomes a
strict comparison against the empty string.

In `@apps/browser-extension/src/utils/trpc.ts`:
- Around line 53-55: The thrown Error in utils/trpc.ts embeds the raw `preview`
response into a user-facing message; remove the raw snippet from the Error text
and replace it with a sanitized, generic message (e.g., "Server returned an
unexpected response.") combined with the existing
`SERVER_ADDRESS_ERROR_MESSAGE`; meanwhile send the raw `preview` to an internal
debug log (e.g., `console.debug` or your app logger) before throwing so
developers can inspect it without exposing it to users. Target the throw site
that references `preview` and `SERVER_ADDRESS_ERROR_MESSAGE` and ensure no other
user-facing errors include un-sanitized payloads.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be403cc3-df72-430c-a58b-0f8bee11a1e4

📥 Commits

Reviewing files that changed from the base of the PR and between 53a4598 and 34d0494.

📒 Files selected for processing (6)
  • apps/browser-extension/src/NotConfiguredPage.tsx
  • apps/browser-extension/src/OptionsPage.tsx
  • apps/browser-extension/src/SignInPage.tsx
  • apps/browser-extension/src/utils/settings.ts
  • apps/browser-extension/src/utils/trpc.ts
  • apps/browser-extension/src/utils/url.ts

Comment thread apps/browser-extension/src/NotConfiguredPage.tsx Outdated
Comment thread apps/browser-extension/src/NotConfiguredPage.tsx Outdated
Comment thread apps/browser-extension/src/utils/trpc.ts
Comment thread apps/browser-extension/src/utils/url.ts Outdated
… in NotConfiguredPage

- Fix warning never shown: when address is normalized, update input field
  and show warning instead of navigating away immediately
- Add try/catch around normalizeServerAddress to handle potential exceptions
- Let user review correction before confirming
- Normalize content-type header to lowercase for consistent JSON detection
- Simplify error messages to avoid duplication
- Fix URL scheme validation to be case-insensitive (consistent with isHttpUrl)
- Use transform instead of refine for address validation to prevent silent settings reset
Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
apps/browser-extension/src/utils/trpc.ts (1)

66-75: Consider tightening the JSON parse error heuristic.

The check for "position" without "json" (line 71) is broad and could match unrelated errors (e.g., "Unexpected value at position 5 in array"). This may cause false positives where users see server address guidance for unrelated issues.

Suggested refinement
 export function isJsonParseError(error: unknown): boolean {
   if (error instanceof Error) {
     const message = error.message.toLowerCase();
     return (
-      message.includes("unexpected") &&
-      (message.includes("json") || message.includes("position"))
+      message.includes("json") &&
+      (message.includes("unexpected") || message.includes("parse"))
     );
   }
   return false;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/browser-extension/src/utils/trpc.ts` around lines 66 - 75, The current
isJsonParseError(error: unknown) heuristic is too broad because matching
"position" alone can produce false positives; update isJsonParseError to only
return true for clearer JSON parse indicators (e.g., require "unexpected token",
"unexpected", "invalid character", or the presence of both "position" and
"json"/"token"), or explicitly check for phrases like "unexpected token" and
"invalid character" in the error.message (still normalizing to lowercase) so
unrelated errors containing "position" no longer trigger the JSON parse
guidance; modify the logic inside isJsonParseError to tighten these checks
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/browser-extension/src/utils/settings.ts`:
- Around line 12-21: The address transform in the Zod schema (the address
field's z.string().transform) only validates but leaves full URLs like
https://host/api/v1 intact; update the transform to normalize and persist only
the origin/base URL: if isHttpUrl(val) parse it with URL (catch errors), then
return url.origin (so protocol+host[:port] only) after trimming whitespace; if
parsing fails or val is falsy, return the default "https://cloud.karakeep.app".
Keep the existing isHttpUrl check and default fallback but replace returning val
with the normalized origin to ensure persisted settings never contain path
components.

---

Nitpick comments:
In `@apps/browser-extension/src/utils/trpc.ts`:
- Around line 66-75: The current isJsonParseError(error: unknown) heuristic is
too broad because matching "position" alone can produce false positives; update
isJsonParseError to only return true for clearer JSON parse indicators (e.g.,
require "unexpected token", "unexpected", "invalid character", or the presence
of both "position" and "json"/"token"), or explicitly check for phrases like
"unexpected token" and "invalid character" in the error.message (still
normalizing to lowercase) so unrelated errors containing "position" no longer
trigger the JSON parse guidance; modify the logic inside isJsonParseError to
tighten these checks accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 471b6d31-5ab6-4df5-b1cd-dfc19555baba

📥 Commits

Reviewing files that changed from the base of the PR and between c1664a0 and d3a0b9a.

📒 Files selected for processing (3)
  • apps/browser-extension/src/utils/settings.ts
  • apps/browser-extension/src/utils/trpc.ts
  • apps/browser-extension/src/utils/url.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/browser-extension/src/utils/url.ts

Comment thread apps/browser-extension/src/utils/settings.ts
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.

Karakeep API login error

1 participant