Skip to content

fix: support for IPv6 address literals in HOST env var#2614

Open
kouellette wants to merge 3 commits intoseerr-team:developfrom
kouellette:host-ipv6-support
Open

fix: support for IPv6 address literals in HOST env var#2614
kouellette wants to merge 3 commits intoseerr-team:developfrom
kouellette:host-ipv6-support

Conversation

@kouellette
Copy link

@kouellette kouellette commented Mar 1, 2026

Description

When pages would build URLs for API requests, process.env.HOST would be directly concatenated with process.env.PORT (e.g., 0.0.0.0:5055). This works for IPv4 address literals but does not work for IPv6 address literals because :::5055 for example is invalid. This commit adds a check if process.env.HOST contains a colon (:) and if so, wraps the host in square brackets. E.g., [::]:5055, which is valid. This allows for users to set the HOST environment variable to IPv6 address literals.

How Has This Been Tested?

This was tested on a Debian 13 VM running kernel 6.12.48+deb13-amd64 for the server and a 13-inch 2022 MacBook Pro with the Arc browser Version 1.135.0 (75383), Chromium Engine Version 145.0.7632.110.

Tested by setting HOST env var to :: and another IPv6 literal and ensured pages loaded as expected and the error observed in the ticket was no longer occurring.

Screenshots / Logs (if applicable)

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • Refactor
    • Centralized host:port resolution for API requests used across the app (public settings, auth, collection, movie and TV requests). URLs are now built consistently; no user-facing behavior changes.
  • Chores
    • Server startup now captures listen errors and exits on failure to make process startup more robust; end-user behavior remains unchanged.

When pages would build URLs for API requests, process.env.HOST would be directly concatenated with
process.env.PORT (e.g., `0.0.0.0:5055`). This works for IPv4 address literals but does not work
for IPv6 address literals because `:::5055` for example is invalid. This commit adds a check if
process.env.HOST contains a colon (:) and if so, wraps the host in square brackets. E.g.,
`[::]:5055`, which is valid. This allows for users to set the HOST environment variable to IPv6
address literals.

fix seerr-team#2612
@kouellette kouellette requested a review from a team as a code owner March 1, 2026 21:33
@coderabbitai
Copy link

coderabbitai bot commented Mar 1, 2026

📝 Walkthrough

Walkthrough

Centralized host:port resolution added via getHostAndPort() (handles IPv6 brackets); several server-side page requests now use it. Server startup now captures the HTTP server instance and adds an error listener that logs and exits on listen errors.

Changes

Cohort / File(s) Summary
URL Helper Utility
src/utils/urlHelper.ts
Added export function getHostAndPort(): string — reads HOST/PORT (defaults localhost, 5055), wraps IPv6 literals containing : in [...], returns <host>:<port>.
App & Pages
src/pages/_app.tsx, src/pages/collection/[collectionId]/index.tsx, src/pages/movie/[movieId]/index.tsx, src/pages/tv/[tvId]/index.tsx
Imported getHostAndPort() and replaced inline environment-based host:port construction with http://${getHostAndPort()}/... for server-side Axios requests; no other control-flow or error-handling changes.
Server Startup
server/index.ts
Store HTTP server instance in httpServer, attach error listener to log listen errors and process.exit(1), and ensure .catch handler exits process on startup error.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I’m a rabbit fixing URLs with care,
Brackets for IPv6, tidy and fair,
Pages call one helper, neat and bright,
Server listens safely through the night,
Hopping off — all bindings now right! 🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Changes to server/index.ts add error handling for express.listen() failures, which is a related reliability improvement but goes slightly beyond the strict scope of the IPv6 support fix. Clarify whether the server error handling changes in server/index.ts are intentional and part of this PR scope, or if they should be separated into a distinct improvement PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: support for IPv6 address literals in HOST env var' accurately and clearly summarizes the main change: adding IPv6 address literal support to the HOST environment variable by wrapping IPv6 hosts in brackets.
Linked Issues check ✅ Passed The PR fully addresses issue #2612 by implementing IPv6 bracket wrapping in getHostAndPort() and applying it across all API request URL construction points, eliminating invalid URLs like 'http://:::5055' and enabling valid URLs like 'http://[::]:5055'.

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

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

Copy link

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/urlHelper.ts`:
- Around line 3-5: The current host-wrapping logic in src/utils/urlHelper.ts
blindly brackets any host containing ':' which double-brackets already-bracketed
IPv6 literals; update the guard in the host handling (the block that checks
host.includes(':')) to first detect if host already startsWith('[') and
endsWith(']') and only wrap with `[...]` when those brackets are absent, leaving
already-bracketed values unchanged.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 001f6b1 and ec96997.

📒 Files selected for processing (5)
  • src/pages/_app.tsx
  • src/pages/collection/[collectionId]/index.tsx
  • src/pages/movie/[movieId]/index.tsx
  • src/pages/tv/[tvId]/index.tsx
  • src/utils/urlHelper.ts

Comment on lines +3 to +5
if (host.includes(':')) {
// If host includes a colon, it's an IPv6 literal and needs to be placed in square brackets
host = `[${host}]`;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid double-bracketing pre-bracketed IPv6 values.

If HOST is already configured as a bracketed literal (e.g. [::1]), Line 5 produces [[::1]], which breaks URL construction. Add a guard before wrapping.

💡 Proposed fix
 export function getHostAndPort(): string {
   let host = process.env.HOST || 'localhost';
-  if (host.includes(':')) {
+  if (
+    host.includes(':') &&
+    !(host.startsWith('[') && host.endsWith(']'))
+  ) {
     // If host includes a colon, it's an IPv6 literal and needs to be placed in square brackets
     host = `[${host}]`;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (host.includes(':')) {
// If host includes a colon, it's an IPv6 literal and needs to be placed in square brackets
host = `[${host}]`;
if (
host.includes(':') &&
!(host.startsWith('[') && host.endsWith(']'))
) {
// If host includes a colon, it's an IPv6 literal and needs to be placed in square brackets
host = `[${host}]`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/urlHelper.ts` around lines 3 - 5, The current host-wrapping logic
in src/utils/urlHelper.ts blindly brackets any host containing ':' which
double-brackets already-bracketed IPv6 literals; update the guard in the host
handling (the block that checks host.includes(':')) to first detect if host
already startsWith('[') and endsWith(']') and only wrap with `[...]` when those
brackets are absent, leaving already-bracketed values unchanged.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a shared helper for constructing internal HOST:PORT strings so server-side page requests can correctly target IPv6 address literals (by bracketing them) when building URLs for local API calls.

Changes:

  • Introduced getHostAndPort() utility for consistent internal host/port formatting (including IPv6 bracketing).
  • Updated server-side Axios calls in _app and several detail pages to use the new helper instead of inline process.env.HOST/process.env.PORT string concatenation.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/utils/urlHelper.ts Adds centralized host/port formatting used for internal API URL construction.
src/pages/_app.tsx Switches internal settings/user Axios requests to use the shared host/port helper.
src/pages/movie/[movieId]/index.tsx Uses the shared host/port helper for server-side movie detail API calls.
src/pages/tv/[tvId]/index.tsx Uses the shared host/port helper for server-side TV detail API calls.
src/pages/collection/[collectionId]/index.tsx Uses the shared host/port helper for server-side collection detail API calls.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@0xSysR3ll
Copy link
Contributor

Maybe overkill but this lib could help : https://www.npmjs.com/package/ip-address

@gauthier-th
Copy link
Member

Maybe overkill but this lib could help : https://www.npmjs.com/package/ip-address

Yeah probably overkill. Worst case you don't properly configure the host address and the app crashes at startup?

@kouellette
Copy link
Author

Maybe overkill but this lib could help : https://www.npmjs.com/package/ip-address

Yeah probably overkill. Worst case you don't properly configure the host address and the app crashes at startup?

Correct. However, I noticed this error wasn't being caught and logged so I updated the PR for this. Example logs when HOST=localhost:5055:

2026-03-06T01:13:30.188Z [info]: Starting Seerr version develop-local
2026-03-06T01:13:30.548Z [debug][Settings Migrator]: Checking migration '0001_migrate_hostname.js'...
2026-03-06T01:13:30.549Z [debug][Settings Migrator]: Checking migration '0002_migrate_apitokens.js'...
2026-03-06T01:13:30.550Z [debug][Settings Migrator]: Checking migration '0003_emby_media_server_type.js'...
2026-03-06T01:13:30.551Z [debug][Settings Migrator]: Checking migration '0004_migrate_region_setting.js'...
2026-03-06T01:13:30.551Z [debug][Settings Migrator]: Checking migration '0005_migrate_network_settings.js'...
2026-03-06T01:13:30.552Z [debug][Settings Migrator]: Checking migration '0006_remove_lunasea.js'...
2026-03-06T01:13:30.552Z [debug][Settings Migrator]: Checking migration '0007_migrate_arr_tags.js'...
2026-03-06T01:13:30.553Z [debug][Settings Migrator]: Checking migration '0008_migrate_blacklist_to_blocklist.js'...
2026-03-06T01:13:30.555Z [info][Notifications]: Registered notification agents
2026-03-06T01:13:30.558Z [info][Server]: Skipping starting the scheduled jobs as we have no Plex/Jellyfin/Emby servers setup yet
2026-03-06T01:13:30.652Z [error]: Listen error: getaddrinfo ENOTFOUND localhost:5055 {"errno":-3008,"code":"ENOTFOUND","syscall":"getaddrinfo","hostname":"localhost:5055","stack":"Error: getaddrinfo ENOTFOUND localhost:5055\n    at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)"}
 ELIFECYCLE  Command failed with exit code 1.
 ```

Copy link

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

🧹 Nitpick comments (1)
server/index.ts (1)

281-284: Consider using structured logging for consistency.

The error handler works correctly. For consistency with other logging calls in this file (e.g., lines 270, 276), consider using the structured format with a label property.

♻️ Suggested improvement
 httpServer.on('error', (err) => {
-  logger.error('Listen error:', err);
+  logger.error('Failed to start server', {
+    label: 'Server',
+    message: err.message,
+  });
   process.exit(1);
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/index.ts` around lines 281 - 284, Update the httpServer error handler
to use the same structured logging shape as other calls: replace the freeform
logger.error('Listen error:', err) in the httpServer.on('error', (err) => { ...
}) callback with a single structured call that passes an object including a
label (e.g., label: 'server:listen') and the error (e.g., err) so the log
matches the format used at lines like the other logger.error/logger.info calls;
ensure you only modify the logger.error invocation inside the
httpServer.on('error') handler to emit the label and error fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/index.ts`:
- Around line 281-284: Update the httpServer error handler to use the same
structured logging shape as other calls: replace the freeform
logger.error('Listen error:', err) in the httpServer.on('error', (err) => { ...
}) callback with a single structured call that passes an object including a
label (e.g., label: 'server:listen') and the error (e.g., err) so the log
matches the format used at lines like the other logger.error/logger.info calls;
ensure you only modify the logger.error invocation inside the
httpServer.on('error') handler to emit the label and error fields.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c398c0f-bc14-4470-a352-fefc5235c195

📥 Commits

Reviewing files that changed from the base of the PR and between 9a1113f and 07e80dd.

📒 Files selected for processing (1)
  • server/index.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.

Cannot set HOST env var to "::" to enable IPv6 binding

4 participants