From 7ed4c3ac40800785e94fa2dba55d8464206b6754 Mon Sep 17 00:00:00 2001 From: kjellbergzoey Date: Sun, 7 Jun 2026 17:12:45 +0000 Subject: [PATCH] fix: handle invalid config gracefully in info/logs/open/db-list/watch After zod validation was added, readProjectConfig/readLocalConfig throw when kiqr.yaml or config.yaml exists but is invalid (bad YAML or schema mismatch). Several commands called these without try/catch and crashed with an unhandled error instead of showing the message. Wrap the config reads in info, logs, open, db list, and watch in try/catch and surface the error through each command's existing error path, matching how each file already reports the "not initialized" case. The lib-level config tests already assert readProjectConfig/readLocalConfig throw clear errors on invalid YAML and schema mismatches. Co-Authored-By: Claude Opus 4.8 --- src/commands/db/list.tsx | 9 ++++++++- src/commands/info.tsx | 33 +++++++++++++++++++++++++++------ src/commands/logs.tsx | 9 ++++++++- src/commands/open.tsx | 18 ++++++++++++++++-- src/commands/watch.tsx | 8 +++++++- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/commands/db/list.tsx b/src/commands/db/list.tsx index 1fe62cd..2156f17 100644 --- a/src/commands/db/list.tsx +++ b/src/commands/db/list.tsx @@ -29,7 +29,14 @@ export default function DbList() { useEffect(() => { (async () => { - const pc = readProjectConfig(); + let pc: ReturnType; + try { + pc = readProjectConfig(); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + setTimeout(() => exit(new Error()), 100); + return; + } if (!pc) { setError('This project is not initialized. Run "kiqr init" first.'); setTimeout(() => exit(new Error()), 100); diff --git a/src/commands/info.tsx b/src/commands/info.tsx index 006e40c..f16ccc0 100644 --- a/src/commands/info.tsx +++ b/src/commands/info.tsx @@ -8,18 +8,39 @@ export const description = 'Show project info and credentials'; export default function Info() { const {exit} = useApp(); - const pc = readProjectConfig(); - if (!pc) { + let result: + | { + ok: true; + pc: NonNullable>; + lc: ReturnType; + } + | {ok: false; message: string | null}; + try { + const pc = readProjectConfig(); + if (!pc) { + result = {ok: false, message: null}; + } else { + const lc = readLocalConfig(getProjectRuntimeDir(pc.project_id)); + result = {ok: true, pc, lc}; + } + } catch (err) { + result = {ok: false, message: err instanceof Error ? err.message : String(err)}; + } + + if (!result.ok) { + const message = + result.message ?? 'This project is not initialized. Run "kiqr init" first.'; + if (result.message !== null) { + setTimeout(() => exit(new Error(message)), 50); + } return ( - This project is not initialized. Run "kiqr init" first. + {message} ); } - const runtimeDir = getProjectRuntimeDir(pc.project_id); - const lc = readLocalConfig(runtimeDir); - + const {pc, lc} = result; const hostname = buildProjectHostname(pc.name); const phpMyAdminHostname = buildProjectHostname(pc.name, 'phpmyadmin'); diff --git a/src/commands/logs.tsx b/src/commands/logs.tsx index e05294d..2a7987b 100644 --- a/src/commands/logs.tsx +++ b/src/commands/logs.tsx @@ -11,7 +11,14 @@ export default function Logs() { const {exit} = useApp(); useEffect(() => { - const pc = readProjectConfig(); + let pc: ReturnType; + try { + pc = readProjectConfig(); + } catch (err) { + console.error(err instanceof Error ? err.message : String(err)); + exit(new Error()); + return; + } if (!pc) { console.error('This project is not initialized. Run "kiqr init" first.'); exit(new Error()); diff --git a/src/commands/open.tsx b/src/commands/open.tsx index 6aa4686..ed11e57 100644 --- a/src/commands/open.tsx +++ b/src/commands/open.tsx @@ -67,7 +67,14 @@ export default function Open({args}: Props) { const [appArg] = args; useEffect(() => { - const pc = readProjectConfig(); + let pc: ReturnType; + try { + pc = readProjectConfig(); + } catch (err) { + console.error(err instanceof Error ? err.message : String(err)); + exit(new Error()); + return; + } if (!pc) { console.error('This project is not initialized. Run "kiqr init" first.'); exit(new Error()); @@ -84,7 +91,14 @@ export default function Open({args}: Props) { } const runtimeDir = getProjectRuntimeDir(pc.project_id); - const lc = readLocalConfig(runtimeDir); + let lc: ReturnType; + try { + lc = readLocalConfig(runtimeDir); + } catch (err) { + console.error(err instanceof Error ? err.message : String(err)); + exit(new Error()); + return; + } const hostname = buildProjectHostname(pc.name); const phpMyAdminHostname = buildProjectHostname(pc.name, 'phpmyadmin'); diff --git a/src/commands/watch.tsx b/src/commands/watch.tsx index a4d7b0a..5998daf 100644 --- a/src/commands/watch.tsx +++ b/src/commands/watch.tsx @@ -28,7 +28,13 @@ export default function Watch() { let mounted = true; async function startWatch() { - const pc = readProjectConfig(); + let pc: ReturnType; + try { + pc = readProjectConfig(); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + return; + } if (!pc) { setError('This project is not initialized. Run "kiqr init" first.'); return;