diff --git a/pkl-html-highlighter/Cargo.lock b/pkl-html-highlighter/Cargo.lock
index 1f2a549d48..e6cf05a6ae 100644
--- a/pkl-html-highlighter/Cargo.lock
+++ b/pkl-html-highlighter/Cargo.lock
@@ -59,14 +59,17 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
 [[package]]
 name = "cc"
-version = "1.0.83"
+version = "1.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
-dependencies = [
- "libc",
-]
+checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
 
 [[package]]
 name = "clap"
@@ -120,12 +123,6 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
-[[package]]
-name = "libc"
-version = "0.2.152"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
-
 [[package]]
 name = "memchr"
 version = "2.7.1"
@@ -136,6 +133,7 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
 name = "pkl_html_highlighter"
 version = "0.6.0"
 dependencies = [
+ "anyhow",
  "clap",
  "tree-sitter-highlight",
  "tree-sitter-pkl",
@@ -161,9 +159,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.2"
+version = "1.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -173,9 +171,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -207,18 +205,18 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.56"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.56"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/pkl-html-highlighter/Cargo.toml b/pkl-html-highlighter/Cargo.toml
index 2a9948fd59..10ceaaa5f3 100644
--- a/pkl-html-highlighter/Cargo.toml
+++ b/pkl-html-highlighter/Cargo.toml
@@ -8,6 +8,7 @@ authors = ["pkl-oss@group.apple.com"]
 tree-sitter-highlight = "0.20"
 tree-sitter-pkl = { git = "ssh://git@github.com/apple/tree-sitter-pkl.git", tag = "0.16.0" }
 clap = { version = "4.4.2", features = ["derive"] }
+anyhow = "1.0.40"
 
 [net]
 git-fetch-with-cli = true
diff --git a/pkl-html-highlighter/src/main.rs b/pkl-html-highlighter/src/main.rs
index 22318ae7af..f03e6741ee 100644
--- a/pkl-html-highlighter/src/main.rs
+++ b/pkl-html-highlighter/src/main.rs
@@ -13,12 +13,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // ===----------------------------------------------------------------------===//
-use std::process::exit;
-use std::{fs, io};
-use std::io::Read;
+use anyhow::{Context, Result};
 use clap::Parser;
+use std::fs;
+use std::io::{self, Read};
+use std::process::exit;
 use tree_sitter_highlight::{HighlightConfiguration, Highlighter, HtmlRenderer};
-use tree_sitter_pkl;
 
 #[derive(Parser)]
 struct CliArgs {
@@ -57,72 +57,111 @@ static QUERY_NAMES: [&str; 26] = [
     "title.function.invoke",
     "params",
     "comment",
-    "doctag"
+    "doctag",
 ];
 
-fn highlight_expr(contents: String, highlights_query: String) -> String {
-    let highlighted = highlight(format!("expr = {}", contents), highlights_query, false);
-    let stripped = highlighted.strip_prefix("expr = ").unwrap();
-    return String::from(stripped);
+fn highlight_expr(contents: &str, highlights_query: &str) -> Result {
+    let highlighted = highlight(&format!("expr = {}", contents), highlights_query, false)?;
+    let stripped = highlighted
+        .strip_prefix(
+            "expr = ",
+        )
+        .context("Failed to strip prefix from highlighted output")?;
+    Ok(String::from(stripped))
 }
 
-pub fn highlight(contents: String, highlights_query: String, is_expr: bool) -> String {
+pub fn highlight(contents: &str, highlights_query: &str, is_expr: bool) -> Result {
     // highlight each line as if it were an expression.
     if is_expr {
         return highlight_expr(contents, highlights_query);
     }
+
     let mut pkl_config = HighlightConfiguration::new(
         tree_sitter_pkl::language(),
-        highlights_query.as_str(),
+        highlights_query,
         tree_sitter_pkl::INJECTIONS_QUERY,
         tree_sitter_pkl::LOCALS_QUERY,
-    ).unwrap();
+    )?;
+
     let attrs = &QUERY_NAMES.map(|it| {
         let classes = it
-            .split(".")
+            .split('.')
             .enumerate()
             // The first scope is prefixed with hljs-.
             // Subscopes receive N number of underscores.
             // https://highlightjs.readthedocs.io/en/stable/css-classes-reference.html#a-note-on-scopes-with-sub-scopes
-            .map(|(idx, it)|
-                 if idx == 0 {
-                     format!("hljs-{}", it)
-                 } else {
-                     format!("{}{}", it, "_".repeat(idx))
-                 }
-            )
+            .map(|(idx, it)| {
+                if idx == 0 {
+                    format!("hljs-{}", it)
+                } else {
+                    format!("{}{}", it, "_".repeat(idx))
+                }
+            })
             .collect::>()
             .join(" ");
         format!("class=\"{}\"", classes)
     });
     pkl_config.configure(&QUERY_NAMES);
 
-
     let mut highlighter = Highlighter::new();
-    let events = highlighter.highlight(
-        &pkl_config,
-        contents.as_bytes(),
-        None,
-        |_| None
-    ).unwrap();
+    let events = highlighter.highlight(&pkl_config, contents.as_bytes(), None, |_| None)?;
+
     let mut renderer = HtmlRenderer::new();
-    renderer.render(
-        events,
-        contents.as_bytes(),
-        &move |it| &attrs[it.0].as_bytes()
-    ).unwrap();
-    renderer.lines().collect::>().join("")
+    renderer.render(events, contents.as_bytes(), &move |it| {
+        attrs[it.0].as_bytes()
+    })?;
+    Ok(renderer.lines().collect::>().join(""))
 }
 
-fn main() {
+fn run() -> Result<()> {
     let args: CliArgs = CliArgs::parse();
-    if args.queries.is_none() {
-        print!("Missing required argument: --queries ");
-        exit(1)
-    }
-    let queries = fs::read_to_string(args.queries.unwrap().as_str()).unwrap();
+    let queries_path = args
+        .queries
+        .ok_or_else(|| anyhow::anyhow!("Missing required argument: --queries "))?;
+    let queries = fs::read_to_string(&queries_path)
+        .with_context(|| format!("Failed to read queries from path: {}", queries_path))?;
+
     let mut buf = String::new();
-    io::stdin().read_to_string(&mut buf).expect("Failed to read stdin!");
-    let result = highlight(buf, queries, args.expr);
-    print!("{}", result)
+    io::stdin()
+        .read_to_string(&mut buf)
+        .context("Failed to read from stdin")?;
+    let result = highlight(&buf, &queries, args.expr)?;
+    print!("{}", result);
+    Ok(())
+}
+
+fn main() {
+    if let Err(err) = run() {
+        eprintln!("Error: {:#}", err);
+        exit(1);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    static TEST_HIGHLIGHT_QUERY: &str = "(source_file) @keyword";
+
+    #[test]
+    fn test_highlight_expr() {
+        let expr = "let x = 42";
+        let result = highlight_expr(expr, TEST_HIGHLIGHT_QUERY).unwrap();
+        assert!(result.contains("hljs-operator"));
+    }
+
+    #[test]
+    fn test_highlight_document() {
+        let doc = "let x = 42";
+        let result = highlight(doc, TEST_HIGHLIGHT_QUERY, false).unwrap();
+        assert!(result.contains("hljs-operator"));
+    }
+
+    #[test]
+    fn test_highlight_fails_on_invalid_query() {
+        let doc = "let x = 42";
+        let invalid_query = "(invalid_query) @unknown";
+        let result = highlight(doc, invalid_query, false);
+        assert!(result.is_err());
+    }
 }