{"record":{"id":"bb403e782c6e0750","repo":"remix-run/remix","slug":"expected-a-remix-config-file-or-directory-fromp","errorCode":null,"errorMessage":"Expected a Remix config file or directory: ${fromPath}","messagePattern":"Expected a Remix config file or directory: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/cli/src/lib/remix-config.ts","lineNumber":146,"sourceCode":" * @returns The validated Remix project configuration, or an empty object when no config is found.\n */\nexport async function loadConfig(from: string | URL = process.cwd()): Promise<RemixConfig> {\n  let fromPath = path.resolve(from instanceof URL ? fileURLToPath(from) : from)\n  let stat\n\n  try {\n    stat = await fs.stat(fromPath)\n  } catch (error) {\n    if (isNodeError(error) && error.code === 'ENOENT') throw remixConfigNotFound(fromPath)\n    throw error\n  }\n\n  if (stat.isFile()) {\n    return loadRemixConfig(path.dirname(fromPath), path.basename(fromPath))\n  }\n\n  if (!stat.isDirectory()) {\n    throw new TypeError(`Expected a Remix config file or directory: ${fromPath}`)\n  }\n\n  let configDir = await findAppRoot(fromPath, 'remix.json')\n  return configDir === null ? {} : loadRemixConfig(configDir, undefined)\n}\n\nexport async function loadRemixConfig(\n  cwd: string,\n  configPath: string | undefined,\n): Promise<RemixConfig> {\n  let filePath = path.resolve(cwd, configPath ?? 'remix.json')\n  let text: string\n\n  try {\n    text = await fs.readFile(filePath, 'utf8')\n  } catch (error) {\n    if (isNodeError(error) && error.code === 'ENOENT') {\n      if (configPath === undefined) return {}","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/cli/src/lib/remix-config.ts#L128-L164","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the path points to remix.json or its directory: `ls -l <path>`","Remove pipes/devices and pass a real file path","If generating the path in a script, assert fs.statSync(path).isFile() before calling loadConfig"],"exampleFix":"# before\nloadConfig('/tmp/routes-pipe')\n# after\nloadConfig('/path/to/app/remix.json')","handlingStrategy":"try-catch","validationCode":"let stat = fs.statSync(configPath)\nif (!stat.isFile() && !stat.isDirectory()) throw new TypeError('bad config path')","typeGuard":null,"tryCatchPattern":"try { await loadConfig(p) } catch (e) { if (e instanceof TypeError && e.message.includes('Expected a Remix config')) { /* fix path */ } throw e }","preventionTips":["Assert the config path is a file before loading","Avoid pipes/devices/special files in config paths","Validate dynamically built paths in scripts"],"tags":["config","filesystem","cli"],"backgroundTag":"invalid-config-path","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}