nexu-io/open-design · error · ValueError
Generated chatSpec.ts has an unexpected shape
Error message
Generated chatSpec.ts has an unexpected shape
What it means
Thrown by read_generated_chat_spec() in run_test_matrix.py when the generated src/chatSpec.ts file does not start with 'export const chatSpec = ' or does not end with ' as const;'. The test harness strips those wrappers to recover the JSON object, so any deviation (manual edit, different template, partial write) makes parsing impossible.
Source
Thrown at skills/chat-motion-overlay/scripts/run_test_matrix.py:34
parser.add_argument("--node-modules", required=True, help="Resolved node_modules directory with remotion/react/typescript")
parser.add_argument("--render", action="store_true", help="Render representative still frames with Remotion")
return parser.parse_args()
def write_json(path: Path, payload: dict) -> None:
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
def run(cmd: list[str], cwd: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(cmd, cwd=str(cwd), text=True, capture_output=True)
def read_generated_chat_spec(path: Path) -> dict:
content = path.read_text(encoding="utf-8").strip()
prefix = "export const chatSpec = "
suffix = " as const;"
if not content.startswith(prefix) or not content.endswith(suffix):
raise ValueError("Generated chatSpec.ts has an unexpected shape")
return json.loads(content[len(prefix) : -len(suffix)])
def participant(side: str, preset: str, upload_path: str | None = None) -> dict:
value = {"side": side, "preset": preset}
if upload_path:
value["uploadPath"] = upload_path
return value
def base_config(
*,
container: str = "wechat",
avatar_mode: str = "preset",
device_frame: str = "iphone-dynamic-island",
nickname_mode: str = "hidden",
delivery_format: str = "mov",
show_timestamp: bool = True,View on GitHub (pinned to 5be4028344)
Solutions
- Regenerate the bundle with prepare_chat_overlay_bundle.py so chatSpec.ts is written by write_chat_spec_ts in the canonical shape.
- If you intentionally changed the wrapper, update read_generated_chat_spec's prefix/suffix constants to match.
- Ensure run_test_matrix.py reads from a bundle this tool generated, not a manually authored file.
Example fix
// before (chatSpec.ts after a hand edit)
export const chatSpec: ChatSpec = {...};
// after (regenerated)
export const chatSpec = { ... } as const; Defensive patterns
Strategy: type-guard
Validate before calling
PREFIX = "export const chatSpec = "
SUFFIX = " as const;"
def safe_read_chat_spec(path):
content = path.read_text(encoding="utf-8").strip()
if not content.startswith(PREFIX) or not content.endswith(SUFFIX):
raise ValueError("chatSpec.ts shape mismatch; regenerate the bundle")
import json
return json.loads(content[len(PREFIX):-len(SUFFIX)]) Type guard
def is_canonical_chat_spec_ts(content: str) -> bool:
c = content.strip()
return c.startswith("export const chatSpec = ") and c.endswith(" as const;") Prevention
- Never hand-edit generated chatSpec.ts; regenerate from the builder.
- If you change the wrapper in write_chat_spec_ts, update the reader in lockstep.
When it happens
Trigger: run_test_matrix.py reads <bundle>/src/chatSpec.ts after prepare_chat_overlay_bundle.py produced it, but the file's shape changed — e.g. the template was hand-edited, write_chat_spec_ts was modified to emit a different wrapper, or a stale/foreign chatSpec.ts is present.
Common situations: Editing the remotion template's chatSpec.ts by hand; changing the wrapper format in write_chat_spec_ts without updating the reader; pointing the test matrix at a bundle produced by an older or forked template.
Related errors
- avatarMode=preset does not allow uploadPath for participant
- avatarMode=upload requires uploadPath for participant {speak
- Unsupported avatar key for participant {speaker}: {avatar_ke
- Participant {speaker} appears on both {participant['side']}
- Participant {speaker} has conflicting transcript avatar hint
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/8bb1346a6efdc797.
Report an issue: GitHub.