heygen-com/hyperframes · error · AudioFxChainError

Node is not an object.

Error message

Node ${i} is not an object.

What it means

While mapping over obj.nodes, parseAudioFxChain() checks each element is a non-null object. Any array element that is null, a number, a string, a boolean, or an array is rejected, with the element index interpolated so you can locate it. This runs before any field is read off the node.

Solutions

  1. Make every element of the nodes array a JSON object ({ type, ...params }).
  2. Remove null/placeholder entries before serializing.
  3. Regenerate the chain via serializeAudioFxChain.

Example fix

// before
parseAudioFxChain('{ "version": 1, "nodes": [ {"type":"highpass"}, null ] }'); // Node 1 is not an object

// after
parseAudioFxChain('{ "version": 1, "nodes": [ {"type":"highpass"} ] }');
Defensive patterns

Strategy: validation

Validate before calling

const parsed = JSON.parse(json);
const badIndex = parsed.nodes.findIndex((n: unknown) => typeof n !== "object" || n === null);
if (badIndex !== -1) throw new Error(`node ${badIndex} is not an object`);

Type guard

function isPlainObject(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}

Try / catch

try { parseAudioFxChain(json); }
catch (err) { if (/is not an object/.test(String(err))) { /* drop/fix null entries */ } else throw err; }

Prevention

When it happens

Trigger: A nodes array containing a null placeholder (e.g. trailing comma producing a hole, or JSON null), a bare string id, or a nested array — e.g. [{ type: 'highpass' }, null, { type: 'reverb' }].

Common situations: Hand-editing that leaves a null; a serialization bug that emitted null for a deleted node; copy-paste artifacts; JSON like ["highpass","reverb"] instead of objects.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/6cd27ceffe1434ec. Report an issue: GitHub.

Appendix: source

Thrown at packages/core/src/audioFx.ts:779

  let raw: unknown;
  try {
    raw = JSON.parse(json);
  } catch (err) {
    throw new AudioFxChainError(`Chain file is not valid JSON: ${(err as Error).message}`);
  }
  if (typeof raw !== "object" || raw === null) {
    throw new AudioFxChainError("Chain file must be a JSON object.");
  }
  const obj = raw as { version?: unknown; nodes?: unknown };
  if (obj.version !== HF_AUDIO_FX_CHAIN_VERSION) {
    throw new AudioFxChainError(`Unsupported chain version: ${String(obj.version)}`);
  }
  if (!Array.isArray(obj.nodes)) {
    throw new AudioFxChainError("Chain file is missing a `nodes` array.");
  }
  const nodes: HfAudioFxNode[] = obj.nodes.map((n, i) => {
    if (typeof n !== "object" || n === null) {
      throw new AudioFxChainError(`Node ${i} is not an object.`);
    }
    const node = n as { type?: unknown; enabled?: unknown; params?: unknown };
    if (typeof node.type !== "string" || !BY_ID.has(node.type)) {
      throw new AudioFxChainError(`Node ${i} has unknown effect type: ${String(node.type)}`);
    }
    return {
      type: node.type,
      enabled: node.enabled !== false,
      params: normalizeAudioFxParams(
        node.type,
        (node.params ?? undefined) as HfAudioFxParamValues | undefined,
      ),
    };
  });
  return { version: HF_AUDIO_FX_CHAIN_VERSION, nodes };
}

/** The nodes that should process audio, in order. */

View on GitHub (pinned to c2996c8626)