Introduction

When trying to deploy a Next.js application to Vercel, we encountered a problem where the build succeeded locally but failed on Vercel. The error messages were vague, making it difficult to identify the cause.

This article shares the process from discovering the problem to resolving it, and summarizes what we learned about JavaScript operator precedence.

Symptoms

Error Message

Error occurred prerendering page "/en/smells/22-03"
[Error: An error occurred in the Server Components render.
The specific message is omitted in production builds to avoid leaking sensitive details.]

Notable Behavior

  • Local build succeeds, but Vercel build fails
  • Errors occur on different pages each time (22-03, 24-03, 25-04, etc.)
  • Error details are hidden in production builds

Discovering the Cause

While investigating multiple files, we found the following code pattern:

const origin = process.env.NEXT_PUBLIC_SITE_URL || '' + process.env.NEXT_PUBLIC_BASE_PATH || '';

It looks harmless at first glance, but an operator precedence trap was lurking here.

What Is Operator Precedence?

In JavaScript, operators have precedence. It works the same way as in mathematics, where “multiplication is calculated before addition.”

Precedence Examples

// Same as mathematics
2 + 3 * 4  // -> 2 + 12 -> 14 (3 * 4 is evaluated first)

// In the case of string concatenation (+) and logical OR (||)
// + precedence: 13
// || precedence: 5
// -> + is evaluated first!

Breaking Down the Problematic Code

const origin = process.env.NEXT_PUBLIC_SITE_URL || '' + process.env.NEXT_PUBLIC_BASE_PATH || '';

This code is interpreted as:

const origin = process.env.NEXT_PUBLIC_SITE_URL || ('' + process.env.NEXT_PUBLIC_BASE_PATH) || '';
//                                                  ^ This is evaluated first

When Environment Variables Are Undefined

// NEXT_PUBLIC_SITE_URL = undefined
// NEXT_PUBLIC_BASE_PATH = undefined

const origin = undefined || ('' + undefined) || '';
//             |
const origin = undefined || 'undefined' || '';
//             |
const origin = 'undefined';  // Becomes the string 'undefined'!

'' + undefined becomes the string 'undefined'. This is a result of JavaScript’s type coercion specification.

Why It Succeeded Locally

In the local environment, the environment variables were set, so the problem did not occur:

// Local environment
process.env.NEXT_PUBLIC_SITE_URL = 'http://localhost:3000';

const origin = 'http://localhost:3000' || ('' + undefined) || '';
//             |
const origin = 'http://localhost:3000';  // First value is truthy, so OK

In the Vercel environment, the environment variables were not set, so the string 'undefined' was generated, causing errors in subsequent processing.

Solution

Before Fix

const origin = process.env.NEXT_PUBLIC_SITE_URL || '' + process.env.NEXT_PUBLIC_BASE_PATH || '';

After Fix

const origin = process.env.NEXT_PUBLIC_SITE_URL || process.env.NEXT_PUBLIC_BASE_PATH || '';

By removing the unnecessary '' +, the fallback processing now works as intended.

More Explicit Syntax

You can also use parentheses to make the precedence explicit:

// Make precedence explicit with parentheses
const origin = (process.env.NEXT_PUBLIC_SITE_URL) || (process.env.NEXT_PUBLIC_BASE_PATH) || '';

// Or use the nullish coalescing operator
const origin = process.env.NEXT_PUBLIC_SITE_URL ?? process.env.NEXT_PUBLIC_BASE_PATH ?? '';

Lessons Learned

1. Be Aware of Operator Precedence

OperatorPrecedence
()21 (highest)
+ (string concatenation)13
`
?? (nullish coalescing)4

2. Watch Out for Environment Differences Between Local and Server

  • Just because it works locally does not mean it will work on the server
  • Always be aware of whether environment variables are set
  • Set default values carefully

3. Watch Out for Implicit Type Coercion

'' + undefined  // -> 'undefined' (string)
'' + null       // -> 'null' (string)
'' + 0          // -> '0' (string)
'' + false      // -> 'false' (string)

JavaScript performs type conversion automatically, which can lead to unintended results.

Summary

This bug was an operator precedence issue hidden in seemingly simple code.

// This single-line difference determined whether the build succeeded or failed
A || '' + B || ''   // Bug
A || B || ''        // Correct

You may not usually think much about JavaScript operator precedence. However, caution is needed when combining multiple operators. When in doubt, we recommend using parentheses to write code explicitly.

References