remix-run/remix · error · TypeError
${optionName} values must be strings
Error message
${optionName} values must be strings What it means
Execution-time backstop in `executeDatabaseCommand`: before loading the seed module it re-checks `plan.seed`. Plans built through `resolveDatabaseCommandPlan` are already validated, so hitting this means a hand-constructed or mutated plan reached the executor — an internal invariant or programmatic misuse, not a CLI typo.
Source
Thrown at packages/assets/src/lib/access.ts:146
packageRootsDirty = true
},
inspect,
isAllowed(filePath) {
return inspect(filePath).allowed
},
}
}
function normalizePackageNames(
packageOption: readonly string[] | undefined,
optionName: 'allowPackages',
): Set<string> {
let packageNames = new Set<string>()
for (let packageName of packageOption ?? []) {
if (typeof packageName !== 'string') {
throw new TypeError(`${optionName} values must be strings`)
}
let normalizedPackageName = packageName.trim()
if (!isValidPackageName(normalizedPackageName)) {
throw new TypeError(`${optionName} values must be package names. Received "${packageName}".`)
}
packageNames.add(normalizedPackageName)
}
return packageNames
}
function validatePackageName(packageName: string, message: string): void {
if (!isValidPackageName(packageName)) {
throw new TypeError(message)
}
}View on GitHub (pinned to 9696913134)
Solutions
- Build plans via the planner (`resolveDatabaseCommandPlan`) instead of object literals
- Set `db.seed` in remix.json or pass `--seed` so plans carry the field
- Guard programmatically: `if (plan.command === 'seed' && !plan.seed) throw` before executing
Example fix
// before
let plan = { command: 'seed', db }
executeDatabaseCommand(plan)
// after
let plan = await resolveDatabaseCommandPlan(invocation, dbConfig)
executeDatabaseCommand(plan) Defensive patterns
Strategy: try-catch
Validate before calling
if (plan.command === 'seed' && plan.seed === undefined) {
throw new Error('Seed plan requires a seed path');
} Type guard
function isExecutableSeedPlan(plan: any): plan is { command: 'seed'; seed: string } {
return plan.command === 'seed' && typeof plan.seed === 'string';
} Try / catch
Wrap executeDatabaseCommand in try/catch; on 'requires db.seed' surface guidance to set --seed or db.seed and rebuild the plan via the planner.
Prevention
- Build plans via resolveDatabaseCommandPlan, never by hand
- Assert required plan fields before execution
When it happens
Trigger: Calling `executeDatabaseCommand`/`runDatabaseCommand` programmatically with a plan where `command === 'seed'` and `seed === undefined`, bypassing `resolveDatabaseCommandPlan`.
Common situations: Scripts or tests constructing plan objects by hand; refactors that drop or rename the seed field; partial plans copied between commands.
Related errors
- expected promise to reject, but it resolved with: ${stringif
- ${optionName} values must be package names. Received "${pack
- throw new AssertionError(options)
- ${matcherName} requires a mock function with a .mock.calls p
- throw new AssertionError({ message, actual, expected, operat
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/526ba66378b58158.
Report an issue: GitHub.