{"record":{"id":"b06ce77ce1e7446a","repo":"heygen-com/hyperframes","slug":"unsupported-chain-version-string-obj-version","errorCode":null,"errorMessage":"Unsupported chain version: ${String(obj.version)}","messagePattern":"Unsupported chain version: (.+?)","errorType":"validation","errorClass":"AudioFxChainError","httpStatus":null,"severity":"error","filePath":"packages/core/src/audioFx.ts","lineNumber":772,"sourceCode":"\n/**\n * Parse a chain file. Unknown effect ids are rejected rather than skipped: a\n * chain that silently loses a node would render differently from the project\n * the author saved, which is worse than refusing to render at all.\n */\nexport function parseAudioFxChain(json: string): HfAudioFxChain {\n  let raw: unknown;\n  try {\n    raw = JSON.parse(json);\n  } catch (err) {\n    throw new AudioFxChainError(`Chain file is not valid JSON: ${(err as Error).message}`);\n  }\n  if (typeof raw !== \"object\" || raw === null) {\n    throw new AudioFxChainError(\"Chain file must be a JSON object.\");\n  }\n  const obj = raw as { version?: unknown; nodes?: unknown };\n  if (obj.version !== HF_AUDIO_FX_CHAIN_VERSION) {\n    throw new AudioFxChainError(`Unsupported chain version: ${String(obj.version)}`);\n  }\n  if (!Array.isArray(obj.nodes)) {\n    throw new AudioFxChainError(\"Chain file is missing a `nodes` array.\");\n  }\n  const nodes: HfAudioFxNode[] = obj.nodes.map((n, i) => {\n    if (typeof n !== \"object\" || n === null) {\n      throw new AudioFxChainError(`Node ${i} is not an object.`);\n    }\n    const node = n as { type?: unknown; enabled?: unknown; params?: unknown };\n    if (typeof node.type !== \"string\" || !BY_ID.has(node.type)) {\n      throw new AudioFxChainError(`Node ${i} has unknown effect type: ${String(node.type)}`);\n    }\n    return {\n      type: node.type,\n      enabled: node.enabled !== false,\n      params: normalizeAudioFxParams(\n        node.type,\n        (node.params ?? undefined) as HfAudioFxParamValues | undefined,","sourceCodeStart":754,"sourceCodeEnd":790,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/core/src/audioFx.ts#L754-L790","documentation":"parseAudioFxChain() requires obj.version to exactly equal HF_AUDIO_FX_CHAIN_VERSION (currently 1). Any other value — a different number, a string, undefined, or null — is rejected. Chain files are explicitly versioned so a reader refuses a version it cannot interpret, preventing a future/incompatible chain from rendering with wrong semantics. The bad value is interpolated via String().","triggerScenarios":"Loading a chain file written by a newer HyperFrames (version 2+) on an older runtime; a hand-written file omitting the version field (undefined); a file with version as a string '1' instead of number 1; a file from a different tool that uses its own versioning.","commonSituations":"Version skew between the studio that authored the chain and the renderer; manual authoring that forgets the version key; copy-pasting a chain from docs that target a different version.","solutions":["Set \"version\" to the exact value of HF_AUDIO_FX_CHAIN_VERSION (1) — use the exported constant, do not hard-code.","Upgrade the rendering HyperFrames to match or exceed the version that wrote the chain.","If you genuinely need to migrate an older chain shape, write a one-time transform that rewrites it to the current version before parseAudioFxChain."],"exampleFix":"// before\nparseAudioFxChain('{ \"version\": 2, \"nodes\": [] }'); // Unsupported chain version: 2\n\n// after — pin to the exported version constant\nimport { HF_AUDIO_FX_CHAIN_VERSION } from \"@hyperframes/core\";\nconst json = JSON.stringify({ version: HF_AUDIO_FX_CHAIN_VERSION, nodes: [] });\nparseAudioFxChain(json);","handlingStrategy":"validation","validationCode":"import { HF_AUDIO_FX_CHAIN_VERSION } from \"@hyperframes/core\";\nconst parsed = JSON.parse(json);\nif (parsed.version !== HF_AUDIO_FX_CHAIN_VERSION) {\n  throw new Error(`chain version ${parsed.version} != supported ${HF_AUDIO_FX_CHAIN_VERSION}`);\n}","typeGuard":"import { HF_AUDIO_FX_CHAIN_VERSION } from \"@hyperframes/core\";\nfunction hasMatchingVersion(raw: unknown): boolean {\n  return typeof raw === \"object\" && raw !== null && (raw as any).version === HF_AUDIO_FX_CHAIN_VERSION;\n}","tryCatchPattern":"try { parseAudioFxChain(json); }\ncatch (err) { if (/Unsupported chain version/.test(String(err))) { /* set version to HF_AUDIO_FX_CHAIN_VERSION or upgrade */ } else throw err; }","preventionTips":["Pin version to the exported HF_AUDIO_FX_CHAIN_VERSION constant, never hard-code.","Keep the authoring studio and renderer on the same HyperFrames version.","Migrate older chain shapes with an explicit transform before parse."],"tags":["version","audio-fx-chain","parsing","validation","compatibility"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}