From 37f30993fa933fc5c4b7f9de40d486e39b70070d Mon Sep 17 00:00:00 2001 From: Maciek Szczesniak Date: Thu, 15 Jan 2026 18:53:06 +0100 Subject: [PATCH] fix: show toast error message on ConfigMarkdown parse error (#8049) Co-authored-by: Aiden Cline --- packages/opencode/src/config/config.ts | 35 +++++++++++++++++++++----- packages/opencode/src/skill/skill.ts | 18 ++++++++++--- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 06803879f..134358ec3 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -19,6 +19,8 @@ import { BunProc } from "@/bun" import { Installation } from "@/installation" import { ConfigMarkdown } from "./markdown" import { existsSync } from "fs" +import { Bus } from "@/bus" +import { Session } from "@/session" export namespace Config { const log = Log.create({ service: "config" }) @@ -231,8 +233,15 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) - if (!md.data) continue + const md = await ConfigMarkdown.parse(item).catch((err) => { + const message = ConfigMarkdown.FrontmatterError.isInstance(err) + ? `${err.data.path}: ${err.data.message}` + : `Failed to parse command ${item}` + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + log.error("failed to load command", { command: item, err }) + return undefined + }) + if (!md) continue const patterns = ["/.opencode/command/", "/.opencode/commands/", "/command/", "/commands/"] const file = rel(item, patterns) ?? path.basename(item) @@ -263,8 +272,15 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) - if (!md.data) continue + const md = await ConfigMarkdown.parse(item).catch((err) => { + const message = ConfigMarkdown.FrontmatterError.isInstance(err) + ? `${err.data.path}: ${err.data.message}` + : `Failed to parse agent ${item}` + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + log.error("failed to load agent", { agent: item, err }) + return undefined + }) + if (!md) continue const patterns = ["/.opencode/agent/", "/.opencode/agents/", "/agent/", "/agents/"] const file = rel(item, patterns) ?? path.basename(item) @@ -294,8 +310,15 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) - if (!md.data) continue + const md = await ConfigMarkdown.parse(item).catch((err) => { + const message = ConfigMarkdown.FrontmatterError.isInstance(err) + ? `${err.data.path}: ${err.data.message}` + : `Failed to parse mode ${item}` + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + log.error("failed to load mode", { mode: item, err }) + return undefined + }) + if (!md) continue const config = { name: path.basename(item, ".md"), diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index 1cc3afee9..95a599a54 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -1,4 +1,5 @@ import z from "zod" +import path from "path" import { Config } from "../config/config" import { Instance } from "../project/instance" import { NamedError } from "@opencode-ai/util/error" @@ -7,6 +8,9 @@ import { Log } from "../util/log" import { Global } from "@/global" import { Filesystem } from "@/util/filesystem" import { Flag } from "@/flag/flag" +import { Bus } from "@/bus" +import { TuiEvent } from "@/cli/cmd/tui/event" +import { Session } from "@/session" export namespace Skill { const log = Log.create({ service: "skill" }) @@ -42,10 +46,16 @@ export namespace Skill { const skills: Record = {} const addSkill = async (match: string) => { - const md = await ConfigMarkdown.parse(match) - if (!md) { - return - } + const md = await ConfigMarkdown.parse(match).catch((err) => { + const message = ConfigMarkdown.FrontmatterError.isInstance(err) + ? `${err.data.path}: ${err.data.message}` + : `Failed to parse skill ${match}` + Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + log.error("failed to load skill", { skill: match, err }) + return undefined + }) + + if (!md) return const parsed = Info.pick({ name: true, description: true }).safeParse(md.data) if (!parsed.success) return