remix-run/remix · error · Error
Invalid package.json at ${packageJsonPath}. Expected an obje
Error message
Invalid package.json at ${packageJsonPath}. Expected an object. What it means
The node-tsx loader reads a package.json file while resolving the module format for a file, but the parsed content is not a JSON object (e.g. it is an array, string, number, or null). Since module format detection requires reading the `type` field from an object, remix/node-tsx throws rather than guessing. This almost always means a malformed or hand-edited package.json on disk.
Source
Thrown at packages/node-tsx/src/lib/package-type.ts:46
let packageJsonPath = path.join(directory, 'package.json')
if (fs.existsSync(packageJsonPath)) {
return packageJsonPath
}
let parentDirectory = path.dirname(directory)
if (parentDirectory === directory) {
return null
}
directory = parentDirectory
}
}
function readPackageType(packageJsonPath: string): ModuleFormat {
try {
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
if (!isRecord(packageJson)) {
throw new Error(`Invalid package.json at ${packageJsonPath}. Expected an object.`)
}
return packageJson.type === 'module' ? 'module' : 'commonjs'
} catch (error) {
let nodeError = error as NodeJS.ErrnoException
if (nodeError.code === 'ENOENT' || nodeError.code === 'ENOTDIR') {
return 'commonjs'
}
throw error
}
}
function hasModuleSyntax(source: string): boolean {
if (/\bimport\.meta\b/.test(source)) {
return true
}
View on GitHub (pinned to 9696913134)
Solutions
- Inspect the package.json at the path in the error message and confirm it parses to an object (`node -e "console.log(typeof require('./package.json'))"`)
- Fix or restore the file — usually `git checkout -- <path>/package.json` or reinstall dependencies
- If it is a fixture used for testing module resolution, rewrite it as `{}`
- Validate all package.json files in the project: `npx pnpm install` or a JSON linter
Example fix
// before: contents of ./broken/package.json
"just a string"
// after
{
"type": "module"
} Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs'
function isValidPackageJson(path) {
try {
return typeof JSON.parse(fs.readFileSync(path, 'utf8')) === 'object' && JSON.parse(fs.readFileSync(path, 'utf8')) !== null && !Array.isArray(JSON.parse(fs.readFileSync(path, 'utf8')))
} catch { return false }
} Type guard
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
} Try / catch
Not applicable — fix the file on disk rather than catching.
Prevention
- Keep package.json files under version control and restore via git rather than hand-editing
- Run `pnpm install` after interruptions to repair corrupted files
- Lint fixtures that write package.json for tests
When it happens
Trigger: Running code through remix/node-tsx (or a Remix dev/SSR server using it) where a directory in the path chain contains a package.json whose top-level JSON value is not an object, such as `[]`, `"text"`, `null`, or `123`. ENOENT is handled separately, so the file exists but is structurally invalid.
Common situations: Truncated or corrupted package.json from a killed install, a package.json containing a bare JSON string or comment-style syntax that accidentally parses to a non-object, a fixture/test file written with invalid content, or a pnpm workspace symlink pointing at a broken package.
Related errors
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/dc3d00f7f88a0bad.
Report an issue: GitHub.