{"record":{"id":"691c5d7105c3807a","repo":"PaddlePaddle/PaddleOCR","slug":"ocr-model-selection-must-define-both-detection-and","errorCode":null,"errorMessage":"OCR model selection must define both detection and recognition models.","messagePattern":"OCR model selection must define both detection and recognition models\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"paddleocr-js/packages/core/src/pipelines/ocr/shared.ts","lineNumber":469,"sourceCode":"): Record<string, ModelAsset> {\n  const assets = Object.fromEntries(\n    OCR_MODEL_ROLES.map((role) => [\n      role.assetKey,\n      resolveSelectedAsset(\n        role.assetKey,\n        role.modelRole,\n        role.selectionKey,\n        baseSelection,\n        configSelection,\n        explicitSelection,\n        configAssets,\n        explicitAssets\n      )\n    ])\n  );\n\n  if (Object.values(assets).some((asset) => !asset)) {\n    throw new Error(\"OCR model selection must define both detection and recognition models.\");\n  }\n\n  return assets as Record<string, ModelAsset>;\n}\n\nfunction getExplicitModelSelection(options: Record<string, unknown>): {\n  modelSelection: Record<string, string | null>;\n  assets: Record<string, ModelAsset>;\n} | null {\n  const modelSelection: Record<string, string | null> = {};\n  const assets: Record<string, ModelAsset> = {};\n  let hasAnyOption = false;\n\n  for (const role of OCR_MODEL_ROLES) {\n    const modelName = readAliasedOption(options, role.nameAliases, role.nameLabel) as\n      | string\n      | undefined;\n    const asset = readAliasedOption(options, role.assetAliases, role.assetLabel) as","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr-js/packages/core/src/pipelines/ocr/shared.ts#L451-L487","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst ocr = await PaddleOCR.create({\n  textDetectionModelName: 'PP-OCRv5_mobile_det'\n  // recognition model missing\n});\n\n// after\nconst ocr = await PaddleOCR.create({\n  textDetectionModelName: 'PP-OCRv5_mobile_det',\n  textRecognitionModelName: 'PP-OCRv5_mobile_rec'\n});","handlingStrategy":"validation","validationCode":"// Both roles must resolve before create()\nfunction resolvesBothModels(o: Record<string, unknown>): boolean {\n  const usesDefaults = o.textDetectionModelName === undefined && o.textRecognitionModelName === undefined;\n  if (usesDefaults) return true; // lang/defaults supply both\n  const det = o.textDetectionModelName ?? o.text_detection_model_name;\n  const rec = o.textRecognitionModelName ?? o.text_recognition_model_name;\n  return det !== undefined && rec !== undefined;\n}","typeGuard":"type ModelPairConfig = { textDetectionModelName: string; textRecognitionModelName: string };\nfunction isCompleteModelPair(o: Record<string, unknown>): o is ModelPairConfig {\n  return typeof o.textDetectionModelName === 'string' && typeof o.textRecognitionModelName === 'string';\n}","tryCatchPattern":"try {\n  const ocr = await PaddleOCR.create(opts);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('both detection and recognition')) {\n    // add the missing role's model name (or drop explicit selection to use defaults)\n  }\n  throw e;\n}","preventionTips":["Never set a model selection entry to null to 'disable' a stage — both stages are mandatory.","If specifying one model explicitly, specify both."],"tags":["model-selection","configuration","validation"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}