Repository navigation
Fail repository-ai-translate when the non-batch report has errors - #1107
Conversation
Per-unit OpenAI failures were only stored in the import report, so the CLI printed Finished and exited 0 even when translations were skipped. Co-authored-by: Cursor <cursoragent@cursor.com>
| if (!errorLines.isEmpty()) { | ||
| consoleWriter | ||
| .fg(Color.RED) | ||
| .newLine() | ||
| .a("AI translate failed for ") | ||
| .a(errorLines.size()) | ||
| .a(" text unit(s):") | ||
| .println(); | ||
| for (ImportReportLine line : errorLines) { | ||
| consoleWriter | ||
| .fg(Color.RED) | ||
| .a("- locale: ") | ||
| .a(line.locale()) | ||
| .a(", tmTextUnitId: ") | ||
| .a(line.tmTexUnitId()) | ||
| .a(": ") | ||
| .a(line.error()) | ||
| .println(); | ||
| } | ||
| throw new CommandException( | ||
| "AI translate completed with %d error(s)".formatted(errorLines.size())); | ||
| } |
There was a problem hiding this comment.
RepositoryAiTranslationCommand.java L369-390
This changes the exit contract for automated callers: a single OpenAI timeout now makes the whole run exit non-zero, even though every other text unit was already imported. Jobs that chain steps after this command (with set -e, for example) will stop at that point. Could we add an opt-out flag, such as --fail-on-error (default true), or a --max-errors N threshold, so pipelines can choose to tolerate occasional per-unit failures? At minimum, please call out the new non-zero exit in the release notes.
There was a problem hiding this comment.
This is on purpose. We want to know when to retry. If it doesn't exit with an exit code, we'll never know.
There was a problem hiding this comment.
Documented the non-zero exit as a breaking change in the AI Translate guide (docs/_docs/guides/014-ai-translate.md). We're intentionally not adding an opt-out: callers need the exit code to know when to retry.
| for (String reportLocaleUrl : report.reportLocaleUrls()) { | ||
| RepositoryAiTranslateClient.ProtoAiTranslateGetReportLocaleResponse reportLocale = | ||
| repositoryAiTranslateClient.getReportLocale(reportLocaleUrl); | ||
|
|
||
| if (downloadReport) { | ||
| Path path = Path.of(reportLocaleUrl + ".json"); | ||
| Files.createDirectories(path.getParent()); | ||
| Files.write(path, reportLocale.content()); | ||
| consoleWriter.a("- ").a(path.toAbsolutePath().toString()).println(); | ||
| } | ||
|
|
||
| List<ImportReportLine> lines = | ||
| reportMapper.readValueUnchecked( | ||
| reportLocale.content(), new TypeReference<List<ImportReportLine>>() {}); |
There was a problem hiding this comment.
RepositoryAiTranslationCommand.java L348-361 (payload: AiTranslateService.java L591-592)
Before this PR, the report was only fetched with --download-report. Now every non-batch run downloads every locale report in full. Each ImportReportLine includes the whole ResponsesRequest (prompt, glossary terms, base64 screenshot data URL), repeated on every line of the same screenshot group, only so the CLI can read error. On screenshot-heavy runs that can mean many MB per locale. Could the server expose an error summary instead (an error count plus the failed tmTextUnitIds, either on the report endpoint or in the pollable task output)? Then the full report could stay behind --download-report.
There was a problem hiding this comment.
Agreed that downloading the full locale report (with ResponsesRequest / screenshot payloads) just to read error is heavy — but detecting failures still requires fetching the report today, and we're keeping this PR CLI-only.
Without --download-report, the CLI now prints only a compact summary (errors grouped by locale with affected tmTextUnitIds) instead of dumping per-unit detail. With --download-report, behavior is unchanged (write full JSON + list each failed unit).
A server-side error summary / mid-run signal is tracked separately: LXP-2810.
There was a problem hiding this comment.
RepositoryAiTranslationCommandTest.java L59-111
Could we add two cases:
- downloadReport = true combined with a report error: assert that the locale JSON file is still written (to a temp dir) before the CommandException is thrown. The write block moved into the new loop and isn't tested right now.
- Two locales with a mix of failed and successful lines: assert that the count adds up across locales ("completed with N error(s)") and that lines without errors aren't listed.
There was a problem hiding this comment.
Added both cases in RepositoryAiTranslationCommandTest:
downloadReport = true+ report error — asserts the locale JSON is written beforeCommandException- Two locales with mixed success/failure — asserts the aggregate error count and that successful lines are not listed
| for (ImportReportLine line : errorLines) { | ||
| consoleWriter | ||
| .fg(Color.RED) | ||
| .a("- locale: ") | ||
| .a(line.locale()) | ||
| .a(", tmTextUnitId: ") | ||
| .a(line.tmTexUnitId()) | ||
| .a(": ") | ||
| .a(line.error()) | ||
| .println(); | ||
| } | ||
| throw new CommandException( | ||
| "AI translate completed with %d error(s)".formatted(errorLines.size())); |
There was a problem hiding this comment.
RepositoryAiTranslationCommand.java L377-389
If OpenAI is down, every unit fails with the same message, which prints hundreds of nearly identical lines. Could we group by (locale, error) and list the affected tmTextUnitIds, or cap the output (for example, the first 20 lines plus "... and N more")? Please also put the pollable task id in the CommandException message, e.g. "AI translate completed with %d error(s) (task id: %d)", so the report is easy to find.
There was a problem hiding this comment.
Without --download-report, output is now grouped by (locale, error) with the affected tmTextUnitIds on one line. The CommandException message also includes the pollable task id.
We're not capping line count here — timeouts are slow and these runs rarely have hundreds of units. Early abort when OpenAI is repeatedly unreachable (and a cooperative pollable cancel-request) needs server changes; filed as LXP-2810 and LXP-2811.
Print a compact error summary by default, keep full per-unit output with --download-report, document the non-zero exit, and expand tests. Server-side OpenAI abort/cancel is tracked separately (LXP-2810/LXP-2811). Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
repository-ai-translatepollable finishes, the CLI always loads the import report and fails non-zero when any report line has a non-nullerror(timeouts, OpenAI failures, etc.), so callers do not treat skipped translations as success.--download-report, prints a compact summary (errors grouped by locale with affectedtmTextUnitIds). With--download-report, writes locale JSON files and lists each failed unit in detail.Test plan
./mvnw -pl cli -am test -Dtest=RepositoryAiTranslationCommandTest -Dsurefire.failIfNoSpecifiedTests=falsemojito repository-ai-translateagainst a locale/unit that produces a reporterrorand confirm summary (no Finished), non-zero exit--download-report, confirm JSON files are still written and per-unit error lines are printed