{"record":{"id":"59c268edeb33eb85","repo":"PaddlePaddle/PaddleOCR","slug":"unexpected-rec-output-dims-dims-join","errorCode":null,"errorMessage":"Unexpected rec output dims: [${dims.join(\", \")}]","messagePattern":"Unexpected rec output dims: \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"paddleocr-js/packages/core/src/models/rec.ts","lineNumber":294,"sourceCode":"      }\n    }\n    if (maxIdx > 0 && maxIdx !== prevIdx) {\n      const dictIdx = maxIdx - 1;\n      if (dictIdx >= 0 && dictIdx < charDict.length) {\n        text += charDict[dictIdx];\n        probs.push(maxVal);\n      }\n    }\n    prevIdx = maxIdx;\n  }\n  const score = probs.length ? probs.reduce((a, b) => a + b, 0) / probs.length : 0;\n  return { text, score };\n}\n\nfunction postprocess(output: Tensor, charDict: string[]): Array<{ text: string; score: number }> {\n  const dims = output.dims;\n  if (dims.length !== 3) {\n    throw new Error(`Unexpected rec output dims: [${dims.join(\", \")}]`);\n  }\n  const sampleCount = dims[0];\n  const timeSteps = dims[1];\n  const classes = dims[2];\n  const data = output.data as Float32Array;\n  const stride = timeSteps * classes;\n  const results: Array<{ text: string; score: number }> = [];\n  for (let index = 0; index < sampleCount; index += 1) {\n    results.push(decodeCTCSample(data, index * stride, timeSteps, classes, charDict));\n  }\n  return results;\n}\n","sourceCodeStart":276,"sourceCodeEnd":307,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr-js/packages/core/src/models/rec.ts#L276-L307","documentation":"Thrown by rec postprocess() when the recognition model's output tensor is not rank 3. CTC decoding requires the layout [N, T, C] (batch, timesteps, classes); any other rank (e.g. 2D [T*C] or 4D) cannot be decoded and the code aborts. This is the recognition-side analogue of the det dims guards and indicates a wrong or differently-exported ONNX model.","triggerScenarios":"Loading a non-CTC rec model or a classifier into the rec slot; a re-exported model whose logits were reshaped/squeezed at the graph tail; an ONNX Runtime version altering output rank for dynamic shapes.","commonSituations":"Mixing model versions (rec ONNX from one release with core code from another); custom export adding argmax/greedy-decode nodes into the graph so the raw logits never reach the JS side; wrong model wired via config asset descriptors.","solutions":["Log the dims included in the message and compare to the expected [batch, timesteps, classes]","Use the rec model file bundled with / referenced by this paddleocr-js version","If re-exporting, keep the softmax logits output as rank 3 and do not fold decode ops into the graph","Verify the config's model asset descriptor points at a CRNN/SVTR-style CTC recognition model"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isCtcLogitsDims(dims: readonly number[]): dims is [number, number, number] {\n  return dims.length === 3;\n}","tryCatchPattern":"try { await recModel.predict(cv, crops); } catch (e) {\n  if (e instanceof Error && /Unexpected rec output dims/.test(e.message)) {\n    // wrong rec model or folded decode ops in the graph: use the bundled CTC rec asset\n  } else throw e;\n}","preventionTips":["Only load CRNN/SVTR-style CTC recognition models whose logits output is rank 3 [N,T,C]","Keep decode ops (argmax/CTC) out of the exported graph; decode in JS","Smoke-test one crop after swapping rec model files"],"tags":["paddleocr","onnxruntime","recognition","model-mismatch","tensor-shape","ctc"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}