diff --git a/packages/loopover-miner/lib/discover-cli.js b/packages/loopover-miner/lib/discover-cli.js index 333de85c0d..25fd5c5226 100644 --- a/packages/loopover-miner/lib/discover-cli.js +++ b/packages/loopover-miner/lib/discover-cli.js @@ -200,8 +200,7 @@ export async function runDiscover(args, options = {}) { } return 0; } catch (error) { - console.error(error instanceof Error ? error.message : String(error)); - return 2; + return reportCliFailure(parsed.json, describeCliError(error)); } } diff --git a/test/unit/miner-discover-cli.test.ts b/test/unit/miner-discover-cli.test.ts index ae0d48f0d3..7c223b672b 100644 --- a/test/unit/miner-discover-cli.test.ts +++ b/test/unit/miner-discover-cli.test.ts @@ -427,6 +427,25 @@ describe("runDiscover (#4247)", () => { expect(error).toHaveBeenCalledWith("github_unreachable"); }); + it("#5830: --dry-run --json reports fan-out failures as a parseable {ok:false,error} object", async () => { + const initPortfolioQueue = vi.fn(); + const fetchCandidateIssuesWithSummary = vi.fn(async () => { + throw new Error("github_unreachable"); + }); + const log = vi.spyOn(console, "log").mockImplementation(() => undefined); + + const exitCode = await runDiscover(["acme/widgets", "--dry-run", "--json"], { + nowMs: NOW, + initPortfolioQueue, + fetchCandidateIssuesWithSummary, + }); + + expect(exitCode).toBe(2); + expect(initPortfolioQueue).not.toHaveBeenCalled(); + const payload = JSON.parse(String(log.mock.calls[0]?.[0])); + expect(payload).toEqual({ ok: false, error: "github_unreachable" }); + }); + it("#4847: --dry-run stringifies a thrown non-Error value instead of crashing", async () => { const fetchCandidateIssuesWithSummary = vi.fn(async () => { throw "raw_string_fault";