remix-run/remix · error · TypeError

Expected a Remix config file or directory: ${fromPath}

Error message

Expected a Remix config file or directory: ${fromPath}

What it means

loadConfig accepts a path to a remix config file or its containing directory. If the path exists but is neither a regular file nor a directory (or stat reports something else), this TypeError is thrown.

Source

Thrown at packages/cli/src/lib/remix-config.ts:146

 * @returns The validated Remix project configuration, or an empty object when no config is found.
 */
export async function loadConfig(from: string | URL = process.cwd()): Promise<RemixConfig> {
  let fromPath = path.resolve(from instanceof URL ? fileURLToPath(from) : from)
  let stat

  try {
    stat = await fs.stat(fromPath)
  } catch (error) {
    if (isNodeError(error) && error.code === 'ENOENT') throw remixConfigNotFound(fromPath)
    throw error
  }

  if (stat.isFile()) {
    return loadRemixConfig(path.dirname(fromPath), path.basename(fromPath))
  }

  if (!stat.isDirectory()) {
    throw new TypeError(`Expected a Remix config file or directory: ${fromPath}`)
  }

  let configDir = await findAppRoot(fromPath, 'remix.json')
  return configDir === null ? {} : loadRemixConfig(configDir, undefined)
}

export async function loadRemixConfig(
  cwd: string,
  configPath: string | undefined,
): Promise<RemixConfig> {
  let filePath = path.resolve(cwd, configPath ?? 'remix.json')
  let text: string

  try {
    text = await fs.readFile(filePath, 'utf8')
  } catch (error) {
    if (isNodeError(error) && error.code === 'ENOENT') {
      if (configPath === undefined) return {}

View on GitHub (pinned to 9696913134)

Solutions

  1. Verify the path points to remix.json or its directory: `ls -l <path>`
  2. Remove pipes/devices and pass a real file path
  3. If generating the path in a script, assert fs.statSync(path).isFile() before calling loadConfig

Example fix

# before
loadConfig('/tmp/routes-pipe')
# after
loadConfig('/path/to/app/remix.json')
Defensive patterns

Strategy: try-catch

Validate before calling

let stat = fs.statSync(configPath)
if (!stat.isFile() && !stat.isDirectory()) throw new TypeError('bad config path')

Try / catch

try { await loadConfig(p) } catch (e) { if (e instanceof TypeError && e.message.includes('Expected a Remix config')) { /* fix path */ } throw e }

Prevention

When it happens

Trigger: Calling loadConfig with a path that is a FIFO, socket, device, or a broken entry; less commonly, a race where the path is replaced between stat and classification.

Common situations: Pointing tooling at /dev/null or a named pipe instead of remix.json; passing a symlink to a removed file; scripts that generate the config path dynamically.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/bb403e782c6e0750. Report an issue: GitHub.