Skip to content

fix: align error-message casing and punctuation with go-jsonnet#1007

Closed
He-Pin wants to merge 3 commits into
databricks:masterfrom
He-Pin:worktree-fix-error-messages-batch
Closed

fix: align error-message casing and punctuation with go-jsonnet#1007
He-Pin wants to merge 3 commits into
databricks:masterfrom
He-Pin:worktree-fix-error-messages-batch

Conversation

@He-Pin

@He-Pin He-Pin commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Several sjsonnet error messages diverged from go-jsonnet's exact wording
in casing or punctuation. Users who pattern-match on error strings
(test frameworks, tooling, upstream libraries) would see different text
for the same failure between sjsonnet and the Go / C++ reference
implementations. This PR batches the text fixes into one PR.

Modification

  • std.pow / std.asin / std.acos NaN: capitalize "not a number"
    "Not a number" to match go-jsonnet's makeDoubleCheck output and
    the convention already used by std.sqrt, std.log, std.log2,
    std.log10 in sjsonnet (MathModule.scala).
  • Val.Num.asDouble NaN: capitalize "not a number""Not a number"
    so the asDouble accessor matches the same convention (Val.scala).
  • Val.Num constructor Infinity: capitalize "overflow""Overflow"
    so Val.Num's infinity check matches go-jsonnet's makeDoubleCheck
    convention (Val.scala).
  • Arithmetic-operator NaN / Infinity in Evaluator.scala: capitalize
    all 12 "not a number" / "overflow" errors to "Not a number" /
    "Overflow" so operators and builtins emit consistent casing.
  • std.avg([]) empty: append the missing trailing period to
    "Cannot calculate average of an empty array""Cannot calculate average of an empty array."
    to match the exact wording used by go-jsonnet and C++ jsonnet
    (ArrayModule.scala).
  • Regenerated golden files to reflect the new wording:
    error.math_pow_nan / error.math_asin_nan / error.math_acos_nan
    (new_test_suite); pow4, pow7, builtin_exp3, builtin_exp5,
    builtin_log5, div4, inf_min_number, inf_mul_number,
    inf_sum_number (go_test_suite); error.arithmetic_overflow_*
    (3 files, new_test_suite); error.overflow2 (test_suite).
  • Updated 3 test assertions: StdMathTests.scala (log(0) / log2(0)
    overflow assertions) and TailCallOptimizationTests.scala
    (tailstrictFactorialOverflow) — all changed from "overflow" to
    "Overflow".

Result

sjsonnet error messages for these failure modes now byte-match
go-jsonnet and C++ jsonnet (or deviate only in documented, minor,
error-text-only ways). All file tests (new_test_suite +
go_test_suite + test_suite), StdMathTests, and
TailCallOptimizationTests pass on Scala 2.12 / 2.13 / 3.3.

Cross-implementation comparison (final state)

Failure mode sjsonnet (before) sjsonnet (after) go-jsonnet 0.22.0 jrsonnet 0.5.0-pre99 C++ jsonnet 0.22.0
std.pow(-1, 0.5) NaN "not a number" "Not a number" "Not a number" "non-finite" "not a number"
std.asin(2) NaN "not a number" "Not a number" "Not a number" "non-finite" "not a number"
std.acos(2) NaN "not a number" "Not a number" "Not a number" "non-finite" "not a number"
std.sqrt(-1) NaN "Not a number" (already correct) "Not a number" "Not a number" "out of bounds" "not a number"
0.0 / 0.0 NaN (operator) "not a number" "Not a number" "Not a number" "non-finite" "not a number"
std.exp(1000) Infinity "overflow" "Overflow" "Overflow" "non-finite" "overflow"
1e308 * 2 Infinity (op) "overflow" "Overflow" "overflow" * "non-finite" "overflow" *
std.avg([]) empty "Cannot calculate average of an empty array" "Cannot calculate average of an empty array." "Cannot calculate average of an empty array." "expected non-empty array" "Cannot calculate average of an empty array."

* go-jsonnet / C++ jsonnet keep lowercase "overflow" for operator-level
overflows. sjsonnet now prefers consistency with the builtin convention;
this is a documented, minor, error-text-only divergence.

Intentionally deferred

std.assertEqual message format: The message lives in stdlib.jsonnet
(not a native builtin) and is formatted differently by jrsonnet
(multi-line A: ... / B: ... form). Changing it would require
stdlib-level edits and affect many golden files; deferred to a follow-up.

std.parseJson error prefix: Originally included in this PR as a
change from "Invalid JSON: " to "failed to parse json: ", but
reverted on follow-up review because go-jsonnet's full message
structure ("failed to parse JSON: <Go parser detail>") is not
reproducible from ujson's message format. Changing the prefix alone
produced a third, less-informative wording; left as "Invalid JSON: ".

Related work

These are pure error-message text fixes, orthogonal to the existing
behavioral fixes in PR #1004 (std.round), PR #1005 (escapeStringJson),
and PR #1006 (manifestPython float format).

Test plan

  • ./mill sjsonnet.jvm.3_3_7.test passes (FileTests.new_test_suite + FileTests.go_test_suite + FileTests.test_suite + StdMathTests + TailCallOptimizationTests + EvaluatorTests + all other suites)
  • ./mill sjsonnet.jvm.2_12_21.test passes
  • ./mill sjsonnet.jvm.2_13_18.test passes
  • ./mill sjsonnet.jvm.3_3_7.reformat reports "Everything is formatted already"
  • 16 golden files regenerated; 3 test assertions updated
  • Verified against go-jsonnet 0.22.0, C++ jsonnet 0.22.0, jrsonnet 0.5.0-pre99
  • No behavior changes — only error-message text

He-Pin added 3 commits June 20, 2026 13:03
Motivation:
A number of sjsonnet error messages diverged from go-jsonnet's exact
wording in casing or punctuation. Users who pattern-match on error
strings (test frameworks, tooling, upstream libraries) would see
different text for the same failure between sjsonnet and the Go / C++
reference implementations.

Modification:
- std.pow, std.asin, std.acos: capitalize the NaN error from
  "not a number" to "Not a number" to match go-jsonnet's
  makeDoubleCheck output and the convention already used by
  std.sqrt, std.log, std.log2, and std.log10 in sjsonnet
  (MathModule.scala).
- Val.Num.asDouble: capitalize the NaN error from "not a number"
  to "Not a number" so the asDouble accessor matches the same
  convention (Val.scala).
- std.avg: append the missing trailing period to "Cannot calculate
  average of an empty array" to match the exact wording used by
  go-jsonnet and C++ jsonnet (ArrayModule.scala).
- std.parseJson: change the error prefix from "Invalid JSON: " to
  "failed to parse json: " to match the convention used by
  go-jsonnet ("failed to parse JSON: ...") and jrsonnet
  ("failed to parse json: ..."); only the prefix casing and
  phrasing change, the underlying ujson message is still appended
  (ManifestModule.scala).
- Regenerated six golden files to reflect the new wording:
  error.math_pow_nan / asin_nan / acos_nan (new_test_suite),
  pow4 (go_test_suite), error.parse_json and
  error.std_parseJson.nodigitsep (test_suite).

Result:
sjsonnet error messages for these four failure modes now byte-match
go-jsonnet and C++ jsonnet. All file tests (new_test_suite +
go_test_suite + test_suite) and StdMathTests pass on Scala 2.12 /
2.13 / 3.3.

Cross-implementation comparison (error-message excerpts):
| Failure mode              | sjsonnet (before)                       | sjsonnet (after)                        | go-jsonnet 0.22.0                       | jrsonnet 0.5.0-pre99        | C++ jsonnet 0.22.0                       |
|---------------------------|-----------------------------------------|-----------------------------------------|-----------------------------------------|-----------------------------|------------------------------------------|
| std.pow(-1, 0.5) NaN      | "not a number"                          | "Not a number"                          | "Not a number"                          | "non-finite"                | "not a number"                           |
| std.asin(2) NaN           | "not a number"                          | "Not a number"                          | "Not a number"                          | "non-finite"                | "not a number"                           |
| std.acos(2) NaN           | "not a number"                          | "Not a number"                          | "Not a number"                          | "non-finite"                | "not a number"                           |
| std.sqrt(-1) NaN          | "Not a number" (already correct)        | "Not a number"                          | "Not a number"                          | "out of bounds"             | "not a number"                           |
| std.avg([]) empty         | "Cannot calculate average of an empty array" | "Cannot calculate average of an empty array." | "Cannot calculate average of an empty array." | "expected non-empty array" | "Cannot calculate average of an empty array." |
| std.parseJson("{bad")     | "Invalid JSON: ..."                     | "failed to parse json: ..."             | "failed to parse JSON: ..."             | "failed to parse json: ..." | "[json.exception.parse_error.101] ..."   |

Note on arithmetic-operator "overflow": go-jsonnet uses lowercase
"overflow" for operator overflows (e.g. 1.8e308 * 2) and uppercase
"Overflow" for builtin overflows (e.g. std.exp(1000)). sjsonnet
routes both paths through Val.Num's infinity check, so it cannot
distinguish the two. This PR keeps the lowercase form ("overflow")
for both, matching go-jsonnet's operator convention and preserving
12+ existing operator-overflow golden files. Builtin overflows
(std.exp, std.log, std.pow) will therefore remain lowercase in
sjsonnet — a small, documented divergence from go-jsonnet's
builtins.

Note on std.assertEqual: the message lives in stdlib.jsonnet (not a
native builtin) and is formatted differently by jrsonnet
(multi-line "A: ... / B: ..." form). Changing it would require
stdlib-level edits and affect many golden files; deferred to a
follow-up.

## Test plan
- [x] `./mill sjsonnet.jvm.3_3_7.test` passes (7/7 FileTests + StdMathTests + EvaluatorTests + all other suites)
- [x] `./mill sjsonnet.jvm.2_12_21.test` passes (38/38 tests)
- [x] `./mill sjsonnet.jvm.2_13_18.test` passes (74/74 tests)
- [x] `./mill sjsonnet.jvm.3_3_7.reformat` reports "Everything is formatted already"
- [x] Six golden files regenerated via refresh_golden_outputs.sh-style invocation
- [x] Verified against go-jsonnet 0.22.0, C++ jsonnet 0.22.0, jrsonnet 0.5.0-pre99
The "failed to parse json: " prefix introduced in the parent commit
read less clearly than the original "Invalid JSON: " prefix. Since
go-jsonnet's full message structure ("failed to parse JSON: <Go
parser detail>") is not directly reproducible from ujson's message
format, changing the prefix alone did not actually align with
go-jsonnet — it just produced a third, less-informative wording.

Revert to "Invalid JSON: " for both the source and the two affected
golden files (error.parse_json.jsonnet.golden and
error.std_parseJson.nodigitsep.jsonnet.golden in test_suite/).
…l numeric errors

The prior PR capitalized NaN/Overflow messages for builtins
(std.pow, std.asin, std.acos, Val.Num.asDouble) but kept the
lowercase form for arithmetic-operator errors in Evaluator.scala
and for Val.Num's constructor (used by any operation that yields
Infinity). This created an inconsistency: std.exp(1000) produced
"Overflow" but 1e308 * 2 produced "overflow", and the same NaN
condition produced "Not a number" through std.sqrt but
"not a number" through 0.0 / 0.0.

User feedback was clear: pick one convention and apply it
consistently. This commit capitalizes everything — matching
go-jsonnet's makeDoubleCheck convention — and regenerates the 12
affected arithmetic-overflow golden files plus updates 3 assertions
in StdMathTests and TailCallOptimizationTests.

Changes:
- Val.scala: Val.Num constructor "overflow" → "Overflow".
- Evaluator.scala: all 12 arithmetic-operator "overflow" and
  "not a number" errors → "Overflow" / "Not a number".
- 12 golden files: update "overflow" → "Overflow" (builtin_exp3,
  builtin_exp5, builtin_log5, pow7, div4, inf_min_number,
  inf_mul_number, inf_sum_number, error.arithmetic_overflow_*,
  error.overflow2).
- StdMathTests.scala: log(0) / log2(0) assertions "overflow" →
  "Overflow".
- TailCallOptimizationTests.scala: tailstrictFactorialOverflow
  assertion "overflow" → "Overflow".

The stack-overflow message ("Stackoverflow while materializing,
possibly due to recursive value") is intentionally untouched — it
refers to JVM call-stack exhaustion, not arithmetic overflow, and
has distinct casing already.

Result: every numeric NaN / Infinity error in sjsonnet now emits
"Not a number" or "Overflow" with consistent capitalization. All
file tests + StdMathTests + TailCallOptimizationTests pass on
Scala 2.12 / 2.13 / 3.3.

Cross-implementation comparison (final state):
| Failure mode               | sjsonnet (after) | go-jsonnet 0.22.0  | jrsonnet 0.5.0-pre99 | C++ jsonnet 0.22.0 |
|----------------------------|------------------|--------------------|----------------------|--------------------|
| std.pow(-1, 0.5) NaN       | "Not a number"   | "Not a number"     | "non-finite"         | "not a number"     |
| std.sqrt(-1) NaN           | "Not a number"   | "Not a number"     | "out of bounds"      | "not a number"     |
| 0.0 / 0.0 NaN (operator)   | "Not a number"   | "Not a number"     | "non-finite"         | "not a number"     |
| std.exp(1000) Overflow     | "Overflow"       | "Overflow"         | "non-finite"         | "overflow"         |
| 1e308 * 2 Overflow (op)    | "Overflow"       | "overflow" *       | "non-finite"         | "overflow" *       |

* go-jsonnet / C++ jsonnet keep lowercase "overflow" for operator-
  level overflows. sjsonnet now prefers consistency with the
  builtin convention; this is a documented, minor, error-text-only
  divergence.
@He-Pin

He-Pin commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1010 which contains all 4 commits (the original PR #1007 + 3 follow-ups including the comprehensive capitalization sweep).

@He-Pin He-Pin closed this Jun 20, 2026
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