Skip to content

fix: convert format parse errors from Internal Error to user-facing messages#1076

Draft
He-Pin wants to merge 1 commit into
databricks:masterfrom
He-Pin:fix/format-error-messages
Draft

fix: convert format parse errors from Internal Error to user-facing messages#1076
He-Pin wants to merge 1 commit into
databricks:masterfrom
He-Pin:fix/format-error-messages

Conversation

@He-Pin

@He-Pin He-Pin commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Format string parsing errors (invalid conversion types, truncated format codes, unterminated labels) were thrown as java.lang.Exception in Format.scala, which got caught by the generic exception handler in Interpreter and wrapped as "Internal Error" with a full Java stack trace
  • This PR wraps parseFormatCached and lowerParsedFormat calls in Format.format() with try/catch that converts java.lang.Exception to Error.fail(msg, pos), producing clean user-facing error messages
  • Adds regression tests for both error and success cases

Cross-implementation comparison

Expression sjsonnet (before) sjsonnet (after) go-jsonnet jrsonnet
std.format("%z", [1]) Internal Error + Java stack trace Unrecognized conversion type: z Unrecognised conversion type: z unrecognized conversion type: z
std.format("hello %", []) Internal Error + Java stack trace Truncated format code at end of string Truncated format code. truncated format code
std.format("%(key", [1]) Internal Error + Java stack trace Unterminated ( in format spec Truncated format code. truncated format code
"%z" % [1] (operator) Internal Error + Java stack trace Unrecognized conversion type: z Unrecognised conversion type: z unrecognized conversion type: z

Test plan

  • ./mill 'sjsonnet.jvm[3.3.8]'.test — all 29 tests pass
  • Error test: error.format_invalid_conversion.jsonnet — verifies clean error message for %z
  • Error test: error.format_truncated.jsonnet — verifies clean error message for trailing %
  • Success test: format_error_messages.jsonnet — verifies format operations still work correctly
  • Manual verification: % operator and std.format both produce clean errors

…essages

Motivation:
Format string parsing errors (e.g. `std.format("%z", [1])`, `std.format("hello %", [])`)
were thrown as `java.lang.Exception` in `Format.scala`, which propagated up to the
`Interpreter` where they were caught as generic exceptions and wrapped as "Internal Error"
with a full Java stack trace. This produced confusing output compared to go-jsonnet and
jrsonnet, which emit clean one-line error messages.

Modification:
- Wrap `parseFormatCached` and `lowerParsedFormat` calls in `Format.format()` with
  try/catch that converts `java.lang.Exception` to `Error.fail(msg, pos)`, producing
  clean sjsonnet error messages with position info.
- `Error` exceptions (already-sjsonnet errors) are re-thrown unchanged.

Result:
Format errors now produce clean messages like:
  `sjsonnet.Error: [std.format] Unrecognized conversion type: z`
instead of:
  `sjsonnet.Error: [std.format] Internal Error`
  `Caused by: java.lang.Exception: Unrecognized conversion type: z`
  `    at sjsonnet.Format$... (full Java stack trace)`

Cross-implementation comparison:

| Expression | sjsonnet (before) | sjsonnet (after) | go-jsonnet | jrsonnet |
|---|---|---|---|---|
| `std.format("%z", [1])` | Internal Error + Java trace | Unrecognized conversion type: z | Unrecognised conversion type: z | unrecognized conversion type: z |
| `std.format("hello %", [])` | Internal Error + Java trace | Truncated format code at end of string | Truncated format code. | truncated format code |
| `std.format("%(key", [1])` | Internal Error + Java trace | Unterminated ( in format spec | Truncated format code. | truncated format code |
| `std.format("%2$s", arr)` | Internal Error + Java trace | Unrecognized conversion type: $ | Unrecognised conversion type: $ | unrecognized conversion type: $ |
@He-Pin He-Pin marked this pull request as draft July 3, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant