ZipEncoder: Fixing Null Check Error With Symlink
I am writing this article to address a critical issue I encountered while using the ArchiveFile.symlink constructor in the archive package for Dart. Specifically, I ran into a null check error and incorrect default settings when trying to add symbolic links to a Zip archive. This article details the problem, its causes, expected behavior, and a minimal reproducible example to help others facing the same issue.
The Crash: Null Check Operator
When you're trying to create a symbolic link using ArchiveFile.symlink('name', 'target') and then add it to a ZipEncoder, you might encounter a crash. This crash manifests as a _TypeError, which can be quite frustrating when you're trying to get your archiving process to work smoothly. The issue stems from how the ArchiveFile.symlink constructor is designed, and how it interacts with the ZipEncoder.
The Cause of the Crash
The primary reason for this crash is that the ArchiveFile.symlink constructor sets the symbolicLink property correctly, but it leaves the internal content (_content / _rawContent) as null. Now, when ZipEncoder.add() processes the file, it expects to find content that it can read in order to write the file body. For a symbolic link, this content should ideally be the target path string – the location the link points to. However, because _rawContent is null, the encoder trips over a null check, leading to the aforementioned _TypeError. This is a critical oversight, as it renders the ArchiveFile.symlink constructor essentially unusable in its current state. To put it simply, the encoder expects data to process, but it finds nothing, causing it to crash.
The Stack Trace
To give you a clearer picture of what's happening under the hood, here’s a typical stack trace you might encounter:
_TypeError (Null check operator used on a null value)
package:archive/src/zip_encoder.dart: ... // accessing file.rawContent
This stack trace points directly to the line in zip_encoder.dart where the code attempts to access file.rawContent. Since this value is null, the null check operator throws an error, halting the encoding process. Understanding this stack trace is crucial for debugging and pinpointing the exact location of the issue within the library's code.
Incorrect Defaults: Regular File vs. Symlink
Beyond the crashing issue, there's another significant problem with the ArchiveFile.symlink constructor: it initializes the file mode incorrectly. Even if you were to bypass the null check crash, you'd still run into issues due to these incorrect defaults. Specifically, the constructor defaults to a regular file mode instead of a symbolic link mode, which has implications for how the archive is extracted and interpreted by operating systems.
The Problem with the Default File Mode
Currently, ArchiveFile.symlink sets the mode to 0x1a4 (Regular File: 644). This is problematic because, when the archive is extracted—especially on macOS and Linux—the operating system sees the entry as a regular text file containing the path, rather than a symbolic link. In essence, the link is treated as a file with the path written inside it, defeating the purpose of creating a symbolic link in the first place. This incorrect default mode leads to unexpected behavior and broken links upon extraction.
Compression and Symlinks
Another critical aspect is the compression type. Symlinks, to be correctly recognized by most unzip tools, must be stored with CompressionType.none (STORED). This means they should not be compressed. However, the current constructor doesn't enforce this, which can result in