Error handling
Compiles are asynchronous, so nothing is thrown at you directly:
compileString and compilePath never throw synchronously. Every failure — an engine error, a
Sass error, a timeout, a cancellation, a closed compiler — arrives as a failed future.
try {
var output = compiler.compileString(scss, options).get(30, TimeUnit.SECONDS);
return output.getCss();
} catch (ExecutionException e) {
if (e.getCause() instanceof JsassCompilationException sassError) {
// a real Sass problem: report it to the developer
return "/* " + sassError.getMessage() + " */";
}
throw e;
}
The exception hierarchy
Exception
└── JsassException checked, the root of everything jsass reports
├── JsassCompilationException a compile failed
│ ├── JsassCompilationTimeoutException exceeded its timeout
│ ├── JsassCompilationInterruptedException worker thread was interrupted
│ ├── SassFunctionExecutionException a custom function threw
│ └── SassImporterExecutionException an importer threw
└── AmbiguousImportException one import matched several stylesheets
RuntimeException
└── SassPathTraversalException an import escaped its allowed root
Reading a Sass error
JsassCompilationException keeps the diagnostics dart-sass produced in structured form:
| Getter | Content |
|---|---|
getSassMessage() |
dart-sass's own message, unmodified |
getSourceSpan() |
a SourceSpan record: URL plus 0-indexed start/end line and column |
getSassStack() |
the Sass-level stack trace, when there is one |
getRawV8Stack() |
the JavaScript stack — for debugging jsass itself |
getMessage() renders a sanitized summary: the message, the last path segment of the URL and
a 1-indexed line and column. It never contains an absolute filesystem path or V8 internals, which
makes it safe to put in an HTTP response or a log that leaves the machine. When you want more,
read the structured getters.
var span = e.getSourceSpan();
if (span != null) {
log.error("{} at {}:{}:{}", e.getSassMessage(), span.url(),
span.startLine() + 1, span.startColumn() + 1);
}
Timeouts and interrupts
Both are compilation failures, but they mean different things:
JsassCompilationTimeoutException— the compile ran longer than its effective timeout.getElapsed()returns theDurationthat passed before the engine was terminated.JsassCompilationInterruptedException— something interrupted the worker thread from the outside, typically anexecutor.shutdownNow()during application shutdown.
Neither carries a source span: there is no line of SCSS to blame.
Cancelling the future (future.cancel(true)) asks the engine to abort the running compile, and
the future completes as cancelled rather than with one of the exceptions above.
Warnings and debug messages
@warn and @debug are not errors and never fail a compile. They reach you through a
SassLogger on the options:
var options = StringOptions.builder()
.logger(new SassLogger() {
@Override
public void warn(String message, Options opts) {
log.warn("{}{}", opts.isDeprecation() ? "[deprecated] " : "", message);
}
@Override
public void debug(String message, Options opts) {
log.debug(message);
}
})
.build();
The Options argument adds context: getSpan() locates the message in the source,
isDeprecation() says whether it is a deprecation warning, and getStack() gives the Sass call
stack when one is available. Without a logger, dart-sass's default handling applies.
If deprecation warnings are the problem rather than the signal, silenceDeprecations,
quietDeps and fatalDeprecations on the options are the sharper
tools.
Engine-level logging
JavetSlf4JLogger on the compiler builder is a different channel: it
reports what the JavaScript engine itself is doing, not what your stylesheets say. Attach it when
you are debugging module resolution or an engine failure, not for stylesheet diagnostics.