{"record":{"id":"b4d42a198aecf510","repo":"heygen-com/hyperframes","slug":"unrecognized-json-transcript-format-expected-whis","errorCode":null,"errorMessage":"Unrecognized JSON transcript format. Expected whisper.cpp (transcription[].tokens), OpenAI API (words[]), or normalized ([{text, start, end}]).","messagePattern":"Unrecognized JSON transcript format\\. Expected whisper\\.cpp \\(transcription\\[\\]\\.tokens\\), OpenAI API \\(words\\[\\]\\), or normalized \\(\\[(.+?)\\]\\)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/whisper/normalize.ts","lineNumber":54,"sourceCode":" */\nexport function detectFormat(filePath: string): TranscriptFormat {\n  const ext = extname(filePath).toLowerCase();\n  if (ext === \".srt\") return \"srt\";\n  if (ext === \".vtt\") return \"vtt\";\n  if (ext === \".json\") return detectJsonFormat(JSON.parse(readFileSync(filePath, \"utf-8\")));\n  throw new Error(`Unsupported transcript file extension: ${ext}. Use .json, .srt, or .vtt`);\n}\n\nfunction detectJsonFormat(raw: unknown): TranscriptFormat {\n  if (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n    const obj = raw as Record<string, unknown>;\n    if (obj.transcription && Array.isArray(obj.transcription)) return \"whisper-cpp\";\n    if (obj.words && Array.isArray(obj.words)) return \"openai\";\n  }\n  if (Array.isArray(raw) && raw[0]?.text !== undefined && raw[0]?.start !== undefined) {\n    return \"words-json\";\n  }\n  throw new Error(\n    \"Unrecognized JSON transcript format. Expected whisper.cpp (transcription[].tokens), \" +\n      \"OpenAI API (words[]), or normalized ([{text, start, end}]).\",\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Parsers\n// ---------------------------------------------------------------------------\n\n/**\n * Rejoin word fragments that whisper splits across tokens:\n * - Single capital + lowercase continuation: C + aught -> Caught, G + onna -> Gonna\n * - Word ending in consonant + in': shin + in' -> shinin', hid + in' -> hidin'\n */\nfunction mergeFragments(words: Word[]): void {\n  for (let i = 0; i < words.length - 1; i++) {\n    const curr = words[i];\n    const next = words[i + 1];","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/whisper/normalize.ts#L36-L72","documentation":"Thrown by detectJsonFormat when a .json transcript file was successfully parsed as JSON but its structure doesn't match any of the three recognized formats: whisper-cpp (object with a transcription array), OpenAI API (object with a words array), or normalized words-json (array of objects with text and start fields). This is a content-level validation, not a JSON syntax error.","triggerScenarios":"The JSON file is valid JSON but has a different schema — e.g. a raw array without text/start keys, an object with a segments array but no transcription or words field, or a proprietary caption format. Also triggered by an empty JSON object or empty array.","commonSituations":"Using a transcript from a tool that exports a different JSON schema (e.g. Deepgram, AssemblyAI, Rev); an incomplete or truncated export; a manually-created JSON with wrong field names; a whisper-cpp output from a version that changed its schema.","solutions":["Inspect the JSON structure and map it to one of the three supported schemas (whisper-cpp transcription[], OpenAI words[], or normalized [{text, start, end}]).","Convert the transcript to SRT or VTT format instead, which has a simpler, well-defined structure.","If using whisper-cpp output, ensure you're using the correct output format flag (--output-json with transcription field).","For custom JSON, transform it to the normalized [{text, start, end}] format before passing."],"exampleFix":"// before: [{\"word\": \"hello\", \"ts\": 0}]  (unrecognized schema)\n// after:  [{\"text\": \"hello\", \"start\": 0, \"end\": 0.5}]  (normalized words-json)","handlingStrategy":"type-guard","validationCode":"function isRecognizedJsonFormat(raw: unknown): boolean {\n  if (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n    const obj = raw as Record<string, unknown>;\n    if (Array.isArray(obj.transcription) || Array.isArray(obj.words)) return true;\n  }\n  if (Array.isArray(raw) && raw[0]?.text !== undefined && raw[0]?.start !== undefined) return true;\n  return false;\n}","typeGuard":"function isNormalizedWordsArray(raw: unknown): raw is Array<{ text: string; start: number; end: number }> {\n  return Array.isArray(raw) && raw.every(\n    (item) => typeof item === \"object\" && item !== null &&\n      typeof item.text === \"string\" &&\n      typeof item.start === \"number\" &&\n      typeof item.end === \"number\",\n  );\n}","tryCatchPattern":"try {\n  const format = detectFormat(filePath);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"Unrecognized JSON transcript format\")) {\n    console.error(`${err.message}. Convert to SRT/VTT or use a supported JSON schema.`);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Validate the JSON structure against the three supported schemas before passing it.","When in doubt, convert transcripts to SRT or VTT — simpler and universally parseable.","Inspect the JSON file's top-level keys: look for transcription[], words[], or [{text, start, end}]."],"tags":["whisper","transcript","format","json","validation"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}