From 1fcf500efeec960cc9577f9a5bf84a9e610b8b43 Mon Sep 17 00:00:00 2001 From: bladata1990 Date: Fri, 26 Jun 2026 11:46:35 +0530 Subject: [PATCH] CSA-467 Signed-off-by: bladata1990 --- .../main/kotlin/com/atlan/pkg/serde/csv/CSVReader.kt | 7 ++++++- .../main/kotlin/com/atlan/pkg/util/FileBasedDelta.kt | 11 ++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/serde/csv/CSVReader.kt b/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/serde/csv/CSVReader.kt index 0d54368949..59fda7e535 100644 --- a/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/serde/csv/CSVReader.kt +++ b/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/serde/csv/CSVReader.kt @@ -106,7 +106,12 @@ class CSVReader csv.writeHeader(outputHeaders ?: header) preproc.stream().skip(1).forEach { r: CsvRecord -> val transformed = csvPreprocessor.preprocessRow(r.fields, header, typeIdx, qualifiedNameIdx) - csv.writeRecord(transformed) + // Skip entirely-blank rows (e.g. a trailing ",,,,," line): writing these to the + // preprocessed/.processed output produces an asset with empty typeName and qualifiedName, + // which later fails delta calculation with "Invalid asset identity: ::". (CSA-467) + if (transformed.any { it.isNotBlank() }) { + csv.writeRecord(transformed) + } } preproc.close() } diff --git a/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/util/FileBasedDelta.kt b/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/util/FileBasedDelta.kt index 4b7e4baae2..efe2e1c681 100644 --- a/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/util/FileBasedDelta.kt +++ b/package-toolkit/runtime/src/main/kotlin/com/atlan/pkg/util/FileBasedDelta.kt @@ -180,10 +180,19 @@ class FileBasedDelta( reader.stream().skip(1).forEach { r: CsvRecord -> val values = r.fields val assetIdentity = resolveAsset(values, header) - if (assetIdentity != null) { + // Guard against rows that resolve to a blank typeName and/or qualifiedName (e.g. a trailing + // ",,,,," line that slipped into a previously-written .processed file). Caching such an identity + // serializes its key as "::", which later fails deserialization with + // "Invalid asset identity: ::" while iterating the checksum cache. (CSA-467) + if (assetIdentity != null && + assetIdentity.typeName.isNotBlank() && + assetIdentity.qualifiedName.isNotBlank() + ) { val singleLine = r.fields.joinToString("ยง") val checksum = Hashing.murmur3_128().hashString(singleLine, Charsets.UTF_8).toString() cache.put(assetIdentity, checksum) + } else { + logger.warn { "Skipping row with blank typeName/qualifiedName in $filename: $values" } } } }