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

  1. 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'))"`)
  2. Fix or restore the file — usually `git checkout -- <path>/package.json` or reinstall dependencies
  3. If it is a fixture used for testing module resolution, rewrite it as `{}`
  4. 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

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.