PaddlePaddle/PaddleOCR · error
OCR model selection must define both detection and recogniti
Error message
OCR model selection must define both detection and recognition models.
What it means
After resolving detection and recognition model assets from base defaults, pipelineConfig, and explicit options, the pipeline verifies that every role resolved to a non-null asset. If either the detection or recognition asset is missing, OCR cannot run (both stages are mandatory), so resolveSelectedAssets throws this blanket error.
Source
Thrown at paddleocr-js/packages/core/src/pipelines/ocr/shared.ts:469
): Record<string, ModelAsset> {
const assets = Object.fromEntries(
OCR_MODEL_ROLES.map((role) => [
role.assetKey,
resolveSelectedAsset(
role.assetKey,
role.modelRole,
role.selectionKey,
baseSelection,
configSelection,
explicitSelection,
configAssets,
explicitAssets
)
])
);
if (Object.values(assets).some((asset) => !asset)) {
throw new Error("OCR model selection must define both detection and recognition models.");
}
return assets as Record<string, ModelAsset>;
}
function getExplicitModelSelection(options: Record<string, unknown>): {
modelSelection: Record<string, string | null>;
assets: Record<string, ModelAsset>;
} | null {
const modelSelection: Record<string, string | null> = {};
const assets: Record<string, ModelAsset> = {};
let hasAnyOption = false;
for (const role of OCR_MODEL_ROLES) {
const modelName = readAliasedOption(options, role.nameAliases, role.nameLabel) as
| string
| undefined;
const asset = readAliasedOption(options, role.assetAliases, role.assetLabel) asView on GitHub (pinned to 2661c7c0ef)
Solutions
- Provide both detection and recognition models explicitly: textDetectionModelName + textRecognitionModelName (and matching dirs if self-hosted).
- If you nullified a selection to disable a stage, remove that — both stages are required.
- Simplest fix: pass just lang (or nothing) so DEFAULT_MODEL_SELECTION supplies both models.
Example fix
// before
const ocr = await PaddleOCR.create({
textDetectionModelName: 'PP-OCRv5_mobile_det'
// recognition model missing
});
// after
const ocr = await PaddleOCR.create({
textDetectionModelName: 'PP-OCRv5_mobile_det',
textRecognitionModelName: 'PP-OCRv5_mobile_rec'
}); Defensive patterns
Strategy: validation
Validate before calling
// Both roles must resolve before create()
function resolvesBothModels(o: Record<string, unknown>): boolean {
const usesDefaults = o.textDetectionModelName === undefined && o.textRecognitionModelName === undefined;
if (usesDefaults) return true; // lang/defaults supply both
const det = o.textDetectionModelName ?? o.text_detection_model_name;
const rec = o.textRecognitionModelName ?? o.text_recognition_model_name;
return det !== undefined && rec !== undefined;
} Type guard
type ModelPairConfig = { textDetectionModelName: string; textRecognitionModelName: string };
function isCompleteModelPair(o: Record<string, unknown>): o is ModelPairConfig {
return typeof o.textDetectionModelName === 'string' && typeof o.textRecognitionModelName === 'string';
} Try / catch
try {
const ocr = await PaddleOCR.create(opts);
} catch (e) {
if (e instanceof Error && e.message.includes('both detection and recognition')) {
// add the missing role's model name (or drop explicit selection to use defaults)
}
throw e;
} Prevention
- Never set a model selection entry to null to 'disable' a stage — both stages are mandatory.
- If specifying one model explicitly, specify both.
When it happens
Trigger: An explicit selection that defines only one role, e.g. passing text_detection_model_name without any recognition model while also suppressing defaults (e.g. lang: null), or a pipelineConfig whose model selection nulls out one role.
Common situations: Trying to use a detection-only or recognition-only pipeline (not supported); setting lang: null / model selection entries to null expecting to 'skip' a stage; partial config merges that drop one role.
Related errors
- ${modelRole} model selection must define model_name.
- worker mode does not support a custom fetch implementation.
- Conflicting values provided for ${label}: ${aliases.join(",
- Unknown model asset "${modelName}".
- ${modelRole} in inference.yml declares model_name "${declare
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/691c5d7105c3807a.
Report an issue: GitHub.