Surface structured parse diagnostics from PolicySet.parsePolicies - #367
Open
jamesmulcahy wants to merge 1 commit into
Open
Surface structured parse diagnostics from PolicySet.parsePolicies#367jamesmulcahy wants to merge 1 commit into
jamesmulcahy wants to merge 1 commit into
Conversation
Parse failures are reduced to format!("Internal JNI Error: {e}") in
jni_failed, discarding the miette diagnostic cedar produced: the source
span, the tokens the parser expected, and the help text. ParseErrors'
Display also prints only its first error, so subsequent errors are lost.
Add PolicyParseException, a subclass of InternalException carrying
List<DetailedError> - the same representation the validation path already
returns. parsePoliciesJni downcasts ParseErrors and converts each
ParseError via the existing From<&E: miette::Diagnostic> impl. If building
the richer exception fails for any reason the generic path is used, so a
parse error can never become a different kind of failure.
Existing catch (InternalException) blocks are unaffected. Two message
details change deliberately: the "Internal JNI Error: " prefix is dropped,
since it describes the binding rather than the policy and reads as a
library fault rather than a typo the caller can fix; and getErrors()
carries one entry per parse error rather than a single entry for the whole
document, which is what its plural contract always implied.
Signed-off-by: James Mulcahy <jmulcahy@netflix.com>
jamesmulcahy
force-pushed
the
surface-parse-diagnostics
branch
from
August 24, 2026 16:35
8d0587c to
5276059
Compare
Author
|
Full disclosure -- I've not written any rust before myself, and Claude helped with this change. Background/motivation is well summarized by Claude above. The TL;DR is that the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
PolicySet.parsePoliciesthrowsInternalExceptionwhose message isformat!("Internal JNI Error: {e}")(CedarJavaFFI/src/interface.rs, injni_failed). For a parse failure that discards everythingmietterecorded — the source span, the tokens the parser expected, the help text — and becauseParseErrors'Displayprints only its first error, every subsequent error is lost too.A policy author writing this:
sees only:
No location, and no hint that the mistake is a missing
action ==. The Rust CLI, on the same input:Measured against cedar-java 4.10.0:
Change
Everything needed already exists in the crate:
PolicySet::from_strreturnsParseErrors, which isIntoIterator<Item = ParseError>ParseErrorimplementsmiette::Diagnosticcedar_policy::ffi::DetailedErroralready hasimpl<E: miette::Diagnostic + ?Sized> From<&E>— the impl the validation path already usesSo
parsePoliciesJnidowncastsParseErrorsand throws a newPolicyParseException extends InternalExceptioncarryingList<DetailedError>, the same representationAuthorizationEngine.validatealready returns:Existing
catch (InternalException e)blocks are unaffected. If building the richer exception fails for any reason, the generic path is used, so a parse error can never turn into a different kind of failure.Two message changes, both up for discussion
getMessage()Internal error: Internal JNI Error: unexpected token ...Internal error: unexpected token ...getErrors()[Internal JNI Error: unexpected token ...][unexpected token ...]"Internal JNI Error: "prefix is dropped — it describes the binding rather than the policy, and reading "Internal error" for an ordinary typo suggests a fault in the library rather than something the caller can fix.getErrors()carries one entry per parse error rather than a single entry for the whole document, which is what its plural contract always implied.Both are independent of the diagnostics and easy to drop if you'd rather keep the strings frozen — happy to revise.
Breaking?
Not to any type or signature:
PolicyParseExceptionis a subclass and no existing method changes shape. Callers string-matching on the message text of a parse failure would see the two differences above.Testing
PolicyParseDiagnosticsTests— span covers the offending token, all errors reported not just the first, help text survives where Cedar supplies it, still catchable asInternalException, message/getErrors()strings pinned, valid policy sets unaffected.CedarJavasuite: 68,723 tests, 0 failures.cargo testinCedarJavaFFI: 69 passed.Local FFI builds used
cargo build --release --features partial-evalfor the host target rather than thecargo zigbuildcross-compile path, sincezigwas not available in my environment; CI exercises the normal path.Scope
Deliberately limited to
parsePolicies.Policy.parseStaticPolicy,Policy.parsePolicyTemplate,Schema.parseandPolicyFormatterlose detail the same way and would be natural follow-ups — kept out here to keep the change focused, per CONTRIBUTING.Related: #68 asks for the id of a malformed policy, which is adjacent but stops short of diagnostics.