heygen-com/hyperframes · warning

Speech was generated but metadata could not be read. Check t

Error message

Speech was generated but metadata could not be read. Check the output file manually.

What it means

Synthesis actually succeeded — the WAV exists at outputPath — but JSON.parse on the last line of the Python stdout threw a SyntaxError. The inline script prints a JSON metadata object as its final line; if Python emitted a warning, traceback fragment, or print AFTER that line (or if the JSON was malformed), parsing fails. The library deliberately surfaces a clearer message rather than re-throwing the raw SyntaxError or fabricating metadata.

Source

Thrown at packages/cli/src/tts/synthesize.ts:199

    const jsonLine = lines[lines.length - 1] ?? "";
    const result: {
      outputPath: string;
      sampleRate: number;
      durationSeconds: number;
      langApplied: boolean;
    } = JSON.parse(jsonLine);

    return {
      outputPath: result.outputPath,
      sampleRate: result.sampleRate,
      durationSeconds: result.durationSeconds,
      langApplied: result.langApplied,
    };
  } catch (err: unknown) {
    // If the error is our own JSON parse failure but the file was created,
    // re-throw with a clearer message rather than returning fabricated data
    if (err instanceof SyntaxError && existsSync(outputPath)) {
      throw new Error(
        "Speech was generated but metadata could not be read. Check the output file manually.",
      );
    }

    let detail = "";
    if (err && typeof err === "object" && "stderr" in err) {
      const stderr = String(err.stderr).trim();
      if (stderr) detail = `\n${stderr.slice(-500)}`;
    }
    throw new Error(`Speech synthesis failed${detail}`);
  }
}

View on GitHub (pinned to c2996c8626)

Solutions

  1. The audio file was created — use it directly; the metadata (sampleRate, durationSeconds) can be re-derived from the WAV header if needed.
  2. Set PYTHONWARNINGS=ignore to suppress warnings that may print to stdout after the JSON line.
  3. Inspect the cached synth-v2.py: confirm the json.dumps print is the absolute last statement with no trailing code.
  4. Capture and read the Python stdout yourself to see what came after the JSON line and report the noise source.
Defensive patterns

Strategy: fallback

Try / catch

try {
  return await synthesize(text, out, { voice });
} catch (err) {
  if (err instanceof Error && /metadata could not be read/.test(err.message) && existsSync(out)) {
    // synthesis succeeded — derive metadata from the WAV header instead of failing
    return { outputPath: out, sampleRate: readWavSampleRate(out), durationSeconds: readWavDuration(out), langApplied: false };
  }
  throw err;
}

Prevention

When it happens

Trigger: A Python warning (e.g. from torch/onnxruntime) printed to stdout AFTER the JSON line; the script printed a deprecation warning without a trailing newline ordering; a newer kokoro-onnx prints a progress/info line after the JSON; multi-line stdout where the 'last line' heuristic grabs a partial JSON fragment.

Common situations: User upgraded kokoro-onnx and the new version logs to stdout; PYTHONUNBUFFERED or a logging config that emits to stdout; a NumPy/torch ABI warning that prints at import time after the metadata print; stderr/stdout interleaving under some CI log capturers.

Related errors


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