Skip to content

Commit 280f7dc

Browse files
jrusso1020claude
andcommitted
fix(cli): render --json keeps human output off stdout so a failure answers with the envelope
Under --json a single render printed its header, lint findings and progress on stdout, so a later failure such as `render --variables '{bad' --json` ended with no JSON envelope. --json now silences that output the way --batch --json already did, and --variables is parsed before the browser check so a bad value fails without a Chrome download. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
1 parent edc4df1 commit 280f7dc

4 files changed

Lines changed: 22 additions & 7 deletions

File tree

‎packages/cli/src/cli.agentMode.test.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { spawn } from "node:child_process";
2-
import { mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
2+
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
33
import { tmpdir } from "node:os";
44
import { dirname, join } from "node:path";
55
import { fileURLToPath } from "node:url";
@@ -250,6 +250,18 @@ describe("agent-mode contract", () => {
250250
expect(run.stdout).toContain("No composition found");
251251
});
252252

253+
it("a render that fails under --json prints only the envelope on stdout", async () => {
254+
const project = join(home, "render-json");
255+
mkdirSync(project, { recursive: true });
256+
writeFileSync(join(project, "index.html"), "<!doctype html><html><body></body></html>");
257+
const run = await runAsAgent(["render", project, "--variables", "{bad", "--json"]);
258+
expectQuiet(run);
259+
expect(run.code, run.stderr).toBe(1);
260+
expect(run.stdout.trim().split("\n")).toHaveLength(1);
261+
expectJsonFailure(run, "runtime_error");
262+
expect(JSON.parse(run.stdout).error.message).toBe("Invalid JSON in --variables");
263+
});
264+
253265
it("an unknown command under --json is an envelope with the suggestion", async () => {
254266
const run = await runAsAgent(["rendr", "--json"]);
255267
expect(run.code).toBe(2);

‎packages/cli/src/commands/render.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ export default defineCommand({
347347
},
348348
json: {
349349
type: "boolean",
350-
description: "With --batch, emit exactly one final JSON result document.",
350+
description:
351+
"With --batch, emit exactly one final JSON result document. Without --batch, print no progress, and on failure print the JSON error envelope.",
351352
default: false,
352353
},
353354
resolution: {

‎packages/cli/src/commands/render/execute.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ export async function executeRenderPlan(
9191
}
9292
}
9393

94+
const variables = plan.batchPath
95+
? undefined
96+
: resolveVariablesArg(plan.variablesArg, plan.variablesFileArg);
9497
if (!plan.useDocker) await refuseLowDiskBeforeBrowserDownload(plan);
9598
const browserPath = plan.useDocker
9699
? undefined
@@ -113,13 +116,12 @@ export async function executeRenderPlan(
113116
return;
114117
}
115118

116-
const variables = resolveVariablesArg(plan.variablesArg, plan.variablesFileArg);
117119
if (variables && Object.keys(variables).length > 0) {
118120
const issues = validateVariablesAgainstProject(plan.renderTarget, variables);
119-
reportVariableIssues(issues, { strict: plan.strictVariables, quiet: plan.quiet });
121+
reportVariableIssues(issues, { strict: plan.strictVariables, quiet: plan.effectiveQuiet });
120122
}
121123

122-
if (plan.proresProfile && !PRORES_PROFILES[plan.proresProfile].alpha && !plan.quiet) {
124+
if (plan.proresProfile && !PRORES_PROFILES[plan.proresProfile].alpha && !plan.effectiveQuiet) {
123125
console.log(
124126
c.dim(`ProRes ${plan.proresProfile} has no alpha channel: transparent areas render black.`),
125127
);
@@ -165,7 +167,7 @@ export function renderOptionsFromPlan(
165167
bufferSize: plan.bufferSize,
166168
videoFrameFormat: plan.videoFrameFormat,
167169
pageMotionBlur: plan.pageMotionBlur,
168-
quiet: plan.quiet,
170+
quiet: plan.effectiveQuiet,
169171
browserPath,
170172
debug: plan.debug,
171173
resumeSegments: plan.resumeSegments,

‎packages/cli/src/commands/render/plan.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ export function createRenderPlan(args: RenderCommandArgs, now = new Date()): Ren
585585
keepSegments: args["keep-segments"] ?? false,
586586
bestEffort: args["best-effort"] ?? true,
587587
batchJson,
588-
effectiveQuiet: quiet || (batchPath != null && batchJson),
588+
effectiveQuiet: quiet || batchJson,
589589
strictAll: args["strict-all"] ?? false,
590590
strictErrors: (args.strict ?? false) || (args["strict-all"] ?? false),
591591
crf,

0 commit comments

Comments
 (0)