<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Serverless Scala JAR: Scala UDFs that read `Row` input fail in Data Engineering</title>
    <link>https://community.databricks.com/t5/data-engineering/serverless-scala-jar-scala-udfs-that-read-row-input-fail/m-p/167384#M55698</link>
    <description>&lt;P&gt;### Setup&lt;/P&gt;&lt;P&gt;- Serverless jar task, environment version 4 (Databricks Connect 17.3.2, Scala 2.13.16, JDK 17). Also reproduced on version 5 (Databricks Connect 18.0.0).&lt;BR /&gt;- JAR built with databricks-connect_2.13 as &lt;STRONG&gt;provided&lt;/STRONG&gt;&lt;BR /&gt;- Structured Streaming from a Unity Catalog Delta table, Trigger.AvailableNow(), foreachBatch → Dataset.foreachPartition with a Scala closure that posts rows to an external HTTP service.&lt;/P&gt;&lt;P&gt;### Symptom&lt;/P&gt;&lt;P&gt;Every batch with data fails. The only visible error:&lt;/P&gt;&lt;P&gt;```&lt;BR /&gt;[FOREACH_BATCH_USER_FUNCTION_ERROR] ... [UDF_ERROR.INTERNAL] Execution of function :foreach_batch failed with an&lt;BR /&gt;internal error: INTERNAL: RST_STREAM closed stream. HTTP/2 error code: PROTOCOL_ERROR&lt;BR /&gt;at com.databricks.sql.execution.safespark.ForeachBatchLakeguardSink.close(SafeSparkUDFRunner.scala:945)&lt;BR /&gt;```&lt;/P&gt;&lt;P&gt;### What narrows it down&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="50%"&gt;foreachBatch body&lt;/TD&gt;&lt;TD width="50%"&gt;Result&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.count()&lt;/TD&gt;&lt;TD width="50%"&gt;ok&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.foreachPartition((_: Iterator[Row]) =&amp;gt; ())&lt;/TD&gt;&lt;TD width="50%"&gt;ok, batch commits&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.limit(4).foreachPartition(_.foreach(_ =&amp;gt; ()))&lt;/TD&gt;&lt;TD width="50%"&gt;fails&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.select("&amp;lt;one INT column&amp;gt;").foreachPartition(_.foreach(_ =&amp;gt; ()))&lt;/TD&gt;&lt;TD width="50%"&gt;fails&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.as(Encoders.product[MyCaseClass]).foreachPartition(_.foreach(_ =&amp;gt; ()))&lt;/TD&gt;&lt;TD width="50%"&gt;ok&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;typed case class + full write to the external service&lt;/TD&gt;&lt;TD width="50%"&gt;ok&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;The only difference between pass and fail is whether the closure pulls a &lt;STRONG&gt;Row&lt;/STRONG&gt; from the iterator.&lt;/P&gt;&lt;P&gt;### The real exception&lt;/P&gt;&lt;P&gt;Captured by wrapping the nested call in `Try` inside the `foreachBatch` function and writing the cause chain to a UC volume (the platform never shows it):&lt;/P&gt;&lt;P&gt;```&lt;BR /&gt;org.apache.spark.SparkRuntimeException: [UDF_USER_CODE_ERROR.GENERIC] Execution of function failed.&lt;BR /&gt;UDF error: UDF invocation failed. Error type: class java.lang.NoSuchMethodError.&lt;BR /&gt;Error message: 'boolean org.apache.spark.sql.catalyst.encoders.RowEncoder$.encoderForDataType$default$4()'&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.UDFUtils$.rowEncoder$1(UDFUtils.scala:107)&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.UDFUtils$.getDeserializers(UDFUtils.scala:118)&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.UDFUtils$.$anonfun$invokeIteratorIterator$1(UDFUtils.scala:320)&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.ArrowBasedExecution.processBatches(Payload.scala:124)&lt;BR /&gt;```&lt;/P&gt;&lt;P&gt;Every frame between the throw site and my closure is Databricks code. The &lt;STRONG&gt;Row&lt;/STRONG&gt; deserializer is built lazily on the first next(), which is why empty closures and typed inputs pass.&lt;/P&gt;&lt;P&gt;`RowEncoder$.encoderForDataType` has 2 parameters in Apache Spark 4.0.0 and 3 parameters (only `$default$3`) in Databricks Connect 17.3.2, 18.0.0 and 18.3.4. The sandbox runtime calls `$default$4`, i.e. it was compiled against an internal server-side Spark with a 4-parameter signature that no published client has. Looks like a version skew introduced with the DBR 18-based serverless release.&lt;/P&gt;&lt;P&gt;### Minimal repro&lt;/P&gt;&lt;P&gt;```scala&lt;BR /&gt;spark.readStream.format("delta").table(table)&lt;BR /&gt;.writeStream.option("checkpointLocation", freshVolumePath)&lt;BR /&gt;.trigger(Trigger.AvailableNow())&lt;BR /&gt;.foreachBatch { (batch: DataFrame, _: Long) =&amp;gt;&lt;BR /&gt;batch.foreachPartition((_: Iterator[Row]) =&amp;gt; ()) // passes&lt;BR /&gt;batch.limit(4).foreachPartition((rows: Iterator[Row]) =&amp;gt; rows.foreach(_ =&amp;gt; ())) // NoSuchMethodError&lt;BR /&gt;}&lt;BR /&gt;.start().awaitTermination()&lt;BR /&gt;```&lt;/P&gt;&lt;P&gt;### Questions&lt;/P&gt;&lt;P&gt;1. Is this a known regression, and which serverless release introduced it?&lt;BR /&gt;2. Could the real exception be surfaced instead of `UDF_ERROR.INTERNAL / RST_STREAM`?&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Thu, 03 Sep 2026 08:57:45 GMT</pubDate>
    <dc:creator>ZZX</dc:creator>
    <dc:date>2026-09-03T08:57:45Z</dc:date>
    <item>
      <title>Serverless Scala JAR: Scala UDFs that read `Row` input fail</title>
      <link>https://community.databricks.com/t5/data-engineering/serverless-scala-jar-scala-udfs-that-read-row-input-fail/m-p/167384#M55698</link>
      <description>&lt;P&gt;### Setup&lt;/P&gt;&lt;P&gt;- Serverless jar task, environment version 4 (Databricks Connect 17.3.2, Scala 2.13.16, JDK 17). Also reproduced on version 5 (Databricks Connect 18.0.0).&lt;BR /&gt;- JAR built with databricks-connect_2.13 as &lt;STRONG&gt;provided&lt;/STRONG&gt;&lt;BR /&gt;- Structured Streaming from a Unity Catalog Delta table, Trigger.AvailableNow(), foreachBatch → Dataset.foreachPartition with a Scala closure that posts rows to an external HTTP service.&lt;/P&gt;&lt;P&gt;### Symptom&lt;/P&gt;&lt;P&gt;Every batch with data fails. The only visible error:&lt;/P&gt;&lt;P&gt;```&lt;BR /&gt;[FOREACH_BATCH_USER_FUNCTION_ERROR] ... [UDF_ERROR.INTERNAL] Execution of function :foreach_batch failed with an&lt;BR /&gt;internal error: INTERNAL: RST_STREAM closed stream. HTTP/2 error code: PROTOCOL_ERROR&lt;BR /&gt;at com.databricks.sql.execution.safespark.ForeachBatchLakeguardSink.close(SafeSparkUDFRunner.scala:945)&lt;BR /&gt;```&lt;/P&gt;&lt;P&gt;### What narrows it down&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="50%"&gt;foreachBatch body&lt;/TD&gt;&lt;TD width="50%"&gt;Result&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.count()&lt;/TD&gt;&lt;TD width="50%"&gt;ok&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.foreachPartition((_: Iterator[Row]) =&amp;gt; ())&lt;/TD&gt;&lt;TD width="50%"&gt;ok, batch commits&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.limit(4).foreachPartition(_.foreach(_ =&amp;gt; ()))&lt;/TD&gt;&lt;TD width="50%"&gt;fails&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.select("&amp;lt;one INT column&amp;gt;").foreachPartition(_.foreach(_ =&amp;gt; ()))&lt;/TD&gt;&lt;TD width="50%"&gt;fails&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;batch.as(Encoders.product[MyCaseClass]).foreachPartition(_.foreach(_ =&amp;gt; ()))&lt;/TD&gt;&lt;TD width="50%"&gt;ok&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD width="50%"&gt;typed case class + full write to the external service&lt;/TD&gt;&lt;TD width="50%"&gt;ok&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;The only difference between pass and fail is whether the closure pulls a &lt;STRONG&gt;Row&lt;/STRONG&gt; from the iterator.&lt;/P&gt;&lt;P&gt;### The real exception&lt;/P&gt;&lt;P&gt;Captured by wrapping the nested call in `Try` inside the `foreachBatch` function and writing the cause chain to a UC volume (the platform never shows it):&lt;/P&gt;&lt;P&gt;```&lt;BR /&gt;org.apache.spark.SparkRuntimeException: [UDF_USER_CODE_ERROR.GENERIC] Execution of function failed.&lt;BR /&gt;UDF error: UDF invocation failed. Error type: class java.lang.NoSuchMethodError.&lt;BR /&gt;Error message: 'boolean org.apache.spark.sql.catalyst.encoders.RowEncoder$.encoderForDataType$default$4()'&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.UDFUtils$.rowEncoder$1(UDFUtils.scala:107)&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.UDFUtils$.getDeserializers(UDFUtils.scala:118)&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.UDFUtils$.$anonfun$invokeIteratorIterator$1(UDFUtils.scala:320)&lt;BR /&gt;at com.databricks.spark.safespark.udf.utils.ArrowBasedExecution.processBatches(Payload.scala:124)&lt;BR /&gt;```&lt;/P&gt;&lt;P&gt;Every frame between the throw site and my closure is Databricks code. The &lt;STRONG&gt;Row&lt;/STRONG&gt; deserializer is built lazily on the first next(), which is why empty closures and typed inputs pass.&lt;/P&gt;&lt;P&gt;`RowEncoder$.encoderForDataType` has 2 parameters in Apache Spark 4.0.0 and 3 parameters (only `$default$3`) in Databricks Connect 17.3.2, 18.0.0 and 18.3.4. The sandbox runtime calls `$default$4`, i.e. it was compiled against an internal server-side Spark with a 4-parameter signature that no published client has. Looks like a version skew introduced with the DBR 18-based serverless release.&lt;/P&gt;&lt;P&gt;### Minimal repro&lt;/P&gt;&lt;P&gt;```scala&lt;BR /&gt;spark.readStream.format("delta").table(table)&lt;BR /&gt;.writeStream.option("checkpointLocation", freshVolumePath)&lt;BR /&gt;.trigger(Trigger.AvailableNow())&lt;BR /&gt;.foreachBatch { (batch: DataFrame, _: Long) =&amp;gt;&lt;BR /&gt;batch.foreachPartition((_: Iterator[Row]) =&amp;gt; ()) // passes&lt;BR /&gt;batch.limit(4).foreachPartition((rows: Iterator[Row]) =&amp;gt; rows.foreach(_ =&amp;gt; ())) // NoSuchMethodError&lt;BR /&gt;}&lt;BR /&gt;.start().awaitTermination()&lt;BR /&gt;```&lt;/P&gt;&lt;P&gt;### Questions&lt;/P&gt;&lt;P&gt;1. Is this a known regression, and which serverless release introduced it?&lt;BR /&gt;2. Could the real exception be surfaced instead of `UDF_ERROR.INTERNAL / RST_STREAM`?&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 03 Sep 2026 08:57:45 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/serverless-scala-jar-scala-udfs-that-read-row-input-fail/m-p/167384#M55698</guid>
      <dc:creator>ZZX</dc:creator>
      <dc:date>2026-09-03T08:57:45Z</dc:date>
    </item>
    <item>
      <title>Re: Serverless Scala JAR: Scala UDFs that read `Row` input fail</title>
      <link>https://community.databricks.com/t5/data-engineering/serverless-scala-jar-scala-udfs-that-read-row-input-fail/m-p/167415#M55704</link>
      <description>&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/250564"&gt;@ZZX&lt;/a&gt;&amp;nbsp;,&lt;/P&gt;
&lt;P&gt;First, thank you for the quality of this write-up. The pass/fail matrix, the captured cause chain, and the signature comparison across Apache Spark 4.0.0 and the published Databricks Connect clients make this one of the cleaner bug reports I've seen on this board. You did the hard part already.&lt;/P&gt;
&lt;P&gt;Your diagnosis holds together. The &lt;CODE&gt;$default$4&lt;/CODE&gt; in &lt;CODE&gt;RowEncoder$.encoderForDataType$default$4()&lt;/CODE&gt; is a Scala compiler-generated accessor for a default argument, so a &lt;CODE&gt;NoSuchMethodError&lt;/CODE&gt; there means the server-side sandbox UDF runner was compiled against a Spark build whose &lt;CODE&gt;encoderForDataType&lt;/CODE&gt; carries a fourth parameter, while the classes loaded at execution time only have three. Every failing frame sits in the Databricks safe-execution code, not in your closure or your HTTP client, so this reads as a platform compatibility defect rather than an application problem. It also explains your matrix neatly: the &lt;CODE&gt;Row&lt;/CODE&gt; deserializer is built lazily on the first &lt;CODE&gt;next()&lt;/CODE&gt;, so empty closures, &lt;CODE&gt;count()&lt;/CODE&gt;, and typed Dataset inputs (which use your case class encoder instead of the runtime &lt;CODE&gt;RowEncoder&lt;/CODE&gt;) never touch the broken path. The versions you list also match the public compatibility guidance for environment versions 4 and 5, which further points away from user error.&lt;/P&gt;
&lt;P&gt;On your two questions, here's my honest take:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;
&lt;P&gt;I can't find anything in the public serverless release notes or docs that acknowledges this as a known issue, so I can't confirm which release introduced it or whether a fix is already deployed. Your evidence pointing at a DBR 18-based serverless rollout is plausible, but only Databricks engineering can confirm that from the inside. This deserves a formal support ticket at &lt;A href="https://help.databricks.com" target="_blank"&gt;https://help.databricks.com&lt;/A&gt;. Include the minimal repro, the exact environment version, the resolved &lt;CODE&gt;databricks-connect_2.13&lt;/CODE&gt; version, your JAR dependency tree (confirming Spark and Connect classes stay &lt;CODE&gt;provided&lt;/CODE&gt; and aren't bundled), and the full cause chain you captured. Ask them directly whether the server-side &lt;CODE&gt;RowEncoder&lt;/CODE&gt; signature and the client-visible API are out of sync.&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;P&gt;Agreed that the error surfacing is the second bug here. The real &lt;CODE&gt;NoSuchMethodError&lt;/CODE&gt; being swallowed behind &lt;CODE&gt;UDF_ERROR.INTERNAL / RST_STREAM PROTOCOL_ERROR&lt;/CODE&gt; is the same masking pattern seen in the earlier thread on this board, &lt;A href="https://community.databricks.com/t5/data-engineering/serverless-scala-jar-foreachbatch-fails-with-rst-stream-protocol/td-p/164827" target="_blank"&gt;Serverless Scala JAR: foreachBatch fails with RST_STREAM PROTOCOL_ERROR&lt;/A&gt;, where you added your findings and where @AbhilashNagilla (Databricks) and @GabFernandes shared useful diagnostics guidance. Your trick of wrapping the closure in &lt;CODE&gt;Try&lt;/CODE&gt; and writing the cause chain to a Unity Catalog volume is a genuinely useful workaround for the observability gap, and worth calling out in the ticket as its own issue.&lt;/P&gt;
&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;In the meantime, you've already found the practical workaround: stay on the typed path and avoid materializing &lt;CODE&gt;Row&lt;/CODE&gt; at all. Select only the columns you need and convert to a case class before &lt;CODE&gt;foreachPartition&lt;/CODE&gt;:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class="language-scala"&gt;case class Outbound(id: Long, payload: String)

val outbound = batch
  .select("id", "payload")
  .as[Outbound](Encoders.product[Outbound])

outbound.foreachPartition { rows =&amp;gt;
  // create or reuse the HTTP client per partition, then send rows
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This sidesteps the runtime &lt;CODE&gt;RowEncoder&lt;/CODE&gt; entirely, and your own table shows the full typed write to the external service commits cleanly. Keeping the projection narrow also keeps the Arrow transfer lean. If your schema is genuinely dynamic and can't be represented by a typed projection, the safer temporary option is running this workload on classic dedicated compute, where &lt;CODE&gt;foreachBatch&lt;/CODE&gt; executes in the driver JVM. Note that standard access mode also runs on Spark Connect, so dedicated is the mode that truly bypasses this path.&lt;/P&gt;
&lt;P&gt;A few references for anyone landing here later:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/aws/en/jobs/how-to/use-jars-in-workflows" target="_blank"&gt;Create and run JARs on serverless compute&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/aws/en/dev-tools/databricks-connect/requirements" target="_blank"&gt;Databricks Connect usage requirements&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/aws/en/release-notes/serverless/" target="_blank"&gt;Serverless environment versions&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/aws/en/release-notes/serverless/environment-version/four" target="_blank"&gt;Serverless environment version 4 release notes&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/aws/en/compute/serverless/limitations" target="_blank"&gt;Serverless compute limitations&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://help.databricks.com/" target="_blank"&gt;Databricks Support&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The takeaway: your client build is correct, the skew appears to live server-side, and typed encoders are the safe path until a fix ships. Please do open that support case, and if you hear back on which release introduced the regression, posting the answer here would help the next fella who hits this.&lt;/P&gt;
&lt;P&gt;Regards, Louis&lt;/P&gt;</description>
      <pubDate>Thu, 03 Sep 2026 13:19:15 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/serverless-scala-jar-scala-udfs-that-read-row-input-fail/m-p/167415#M55704</guid>
      <dc:creator>Louis_Frolio</dc:creator>
      <dc:date>2026-09-03T13:19:15Z</dc:date>
    </item>
  </channel>
</rss>

