prisma/prisma · warning
PN_CONTRACT_TYPED_FALLBACK_AVAILABLE
PN_CONTRACT_TYPED_FALLBACK_AVAILABLE
Error message
Contract field "${modelName}.${fieldName}" uses field.namedType('${fieldState.typeRef}'). Use field.namedType(types.${fieldState.typeRef}) when the storage type is declared in the same contract to keep autocomplete and typed local refs. What it means
Emitted (≤5 occurrences, individually) by `emitTypedNamedTypeFallbackWarnings` when a contract field is authored with `field.namedType('MyType')` (string form) but `MyType` is a storage type declared in the same contract and therefore reachable via the typed `types.MyType` token. The string form works at runtime but loses autocomplete and literal-type propagation, so the library nudges toward the typed form. It is a `process.emitWarning` (Node warning), not a thrown error.
Source
Thrown at packages/2-sql/2-authoring/contract-ts/src/contract-warnings.ts:114
relation: Extract<RelationState, { kind: 'manyToMany' }>,
throughDisplay: string,
): string {
const targetDisplay = formatRelationModelDisplay(relation.toModel);
const from = formatFieldSelection(normalizeRelationFieldNames(relation.from));
const to = formatFieldSelection(normalizeRelationFieldNames(relation.to));
return `rel.manyToMany(${targetDisplay}, { through: ${throughDisplay}, from: ${from}, to: ${to} })`;
}
const WARNING_BATCH_THRESHOLD = 5;
function flushWarnings(warnings: readonly string[]): void {
if (warnings.length === 0) {
return;
}
if (warnings.length <= WARNING_BATCH_THRESHOLD) {
for (const message of warnings) {
process.emitWarning(message, { code: 'PN_CONTRACT_TYPED_FALLBACK_AVAILABLE' });
}
return;
}
process.emitWarning(
`${warnings.length} contract references use string fallbacks where typed alternatives are available. ` +
'Use named model tokens and typed storage type refs for autocomplete and type safety.\n' +
warnings.map((w) => ` - ${w}`).join('\n'),
{ code: 'PN_CONTRACT_TYPED_FALLBACK_AVAILABLE' },
);
}
function formatFallbackWarning(location: string, current: string, suggested: string): string {
return (
`Contract ${location} uses ${current}. ` +
`Use ${suggested} when the named model token is available in the same contract to keep typed relation targets and model refs.`
);
}View on GitHub (pinned to 1243cdffbe)
Solutions
- Replace `field.namedType('MyType')` with `field.namedType(types.MyType)` wherever the type is declared in the same contract.
- If the type is genuinely external (not in `storageTypes`), the string form is correct and the warning can be ignored — but verify the type name matches exactly.
- Run the contract emit again to confirm the warning clears for the addressed fields.
Example fix
// before
model.field('email', field.namedType('Email'))
// after
model.field('email', field.namedType(types.Email)) Defensive patterns
Strategy: fallback
Validate before calling
// Before defining a field, prefer the typed token over the string fallback.
// `storageTypes` is the same-contract types map; if the name resolves there, pass the token.
import type { StorageTypeInstance } from '@prisma-next/sql-contract/types';
function safeNamedType(typeRef: string, types: Record<string, StorageTypeInstance>) {
// If declared in this contract, return the typed token; otherwise fall back to the string.
return typeRef in types ? types[typeRef] : typeRef;
}
// Usage: field.namedType(safeNamedType('MyType', contractStorageTypes)) instead of field.namedType('MyType').
// Drive the per-field warning to zero by never passing a bare string when a token exists. Type guard
function hasTypedToken(typeRef: string, types: Record<string, unknown>): typeRef is keyof typeof types & string {
return typeRef in types;
} Try / catch
// This is a process warning (process.emitWarning), not a thrown error.
// Intercept and count it to fail your build instead of letting it pass silently:
const fallbackWarnings: string[] = [];
process.on('warning', (w) => {
if (w.code === 'PN_CONTRACT_TYPED_FALLBACK_AVAILABLE' && !String(w.message).startsWith('Contract field')) return;
if (w.code === 'PN_CONTRACT_TYPED_FALLBACK_AVAILABLE') fallbackWarnings.push(w.message);
});
// After contract build:
if (fallbackWarnings.length > 0) {
throw new Error('Per-field typed-fallback warnings detected; switch string refs to typed tokens.');
} Prevention
- Always pass the typed storage token (`types.MyType`) to `field.namedType(...)` when the storage type is declared in the same contract — reserve the string form for cross-contract/external types only.
- Keep storage type declarations above or within the same contract build so the typed token is in scope at field-definition time.
- Wire a process 'warning' listener in tests/CI that fails on any 'PN_CONTRACT_TYPED_FALLBACK_AVAILABLE' code.
When it happens
Trigger: Authoring a model field with `field.namedType('Email')` where `Email` was registered into the contract's `storageTypes`. The warning fires once per `modelName.fieldName` pair during `emitTypedNamedTypeFallbackWarnings(models, storageTypes)`.
Common situations: Porting hand-written string-based contracts to the typed DSL piecemeal. Copying examples that predate the `types.*` token API. Refactoring a storage type from external to in-contract without updating call sites.
Related errors
AI-assisted analysis of prisma/prisma@1243cdffbe (2026-08-01).
Data as JSON: /data/errors/82088b5789d59c9c.json.
Report an issue: GitHub.