Tackling Synology's Opaque "Invalid File Format" Error

2026-04-05
5 min read
Featured Image

A few months back I bought a new Synology NAS and decided to, on a whim, build an app for it from scratch. Partly to see how well Claude Code could handle it, mostly to see whether the option was open to me. Starting off with the most basic app, the Python backend worked, the tests passed, the SPK archive assembled cleanly, but then Synology’s Package Center rejected it with the error "Invalid file format. Please contact the package developer.". That’s it. No logs, no debug information. Good luck.

This post is an attempt to help the next person who tries to tackle this.

One Opaque Error

What I’m trying to build is IceBox Express, a native Synology DSM 7 package, that archives my Synology Photos iPhone photo backups from my NAS to AWS S3 Glacier Deep Archive for cold storage. It bundles a self-contained Python 3.11 runtime, a Flask web server, and all its pip dependencies as wheels inside a single .spk file - Synology’s proprietary package format. No external dependencies, no SynoCommunity repo required. Upload the SPK, fill in your AWS credentials, and your photos start flowing to cold storage on a schedule.

Synology’s Package Center has a “Manual Install” option: you upload your SPK file, it validates the format, and then more stuff happens, but, er, this isn’t what happened. What I got was:

Invalid file format. Please contact the package developer.

Nothing more, just that message and a dismissive suggestion to contact someone who, in this case, was me.

What an SPK Actually Is

Before we get to the debugging: an SPK file is a tar archive. Not gzipped - plain tar. Inside, it contains an INFO file (key-value metadata like package name, version, architecture), a package.tgz (gzipped tar of the actual application files), lifecycle scripts, icons, and configuration files. The format is not very well documented in Synology’s developer guide, which mostly assumes you’ll use their official toolkit and never look at the raw bytes.

I… I wasn’t using their official toolkit. But for good reason!

Synology’s packaging framework is built around cross-compilation toolchains and C-centric build pipelines - it assumes you’re compiling binaries for a specific NAS architecture. I was just building something for my personal use, which is a pure-Python application with a pre-built interpreter bundled inside. There’s nothing to cross-compile. The entire build is “copy files into a directory tree, tar it up twice.” The community alternative, spksrc, solves a similar but different problem: it builds packages that integrate with SynoCommunity’s repository and dependency infrastructure, which I was deliberately avoiding to keep the package self-contained.

Wanna be super slim? A Makefile, GNU tar, and the developer guide’s description of what goes inside an SPK. What could go wrong, amirite?

The macOS Tar Trap

I sent Claude on a research run, and it found an immediate culprit - an apparently well-known issue in Synology packaging circles:

  • macOS ships BSD tar, which defaults to PAX format.
  • Synology’s parser expects POSIX ustar.

These are not the same.

PAX archives include extended attribute headers (PaxHeaders.0/INFO, etc.) that Synology’s parser doesn’t recognize. I could try to solve this on the local machine, but I knew there was more trouble where that came from, so I moved the entire build into a Docker container running Debian, where GNU tar lives natively.

FROM python:3.11-slim-bullseye
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    curl tar gzip make && \
    rm -rf /var/lib/apt/lists/*

Claude added --format=ustar to the outer tar command for good measure:

tar --format=ustar -cf dist/IceBoxExpress.spk -C build/spk_root .

Rebuilt inside Docker. Transferred the SPK to the NAS. Same error. Dammit.

The Part Where We Start Questioning Everything

I started questioning Claude’s assumptions and sent it to cross its t’s and dot its i’s:

  • Is the outer SPK actually uncompressed tar? Yes - file dist/IceBoxExpress.spk confirmed POSIX tar archive.
  • Is package.tgz actually gzipped? Yes - magic bytes 1f 8b at offset 0.
  • Are the scripts executable? Yes - chmod 755 confirmed, tar -tvf showed rwxr-xr-x.
  • Are the line endings Unix LF? Yes - verified with od.
  • Are the required files present? INFO, package.tgz, scripts/, PACKAGE_ICON.PNG - all present.
  • Are the INFO fields valid? All fields match the spec.

Everything checked out. The SPK looked correct by every metric we could think of. And yet: Invalid file format.

The frustrating part was the total absence of diagnostic information. No DSM log file records why Package Center rejected a package. Just that infuriating error.

We Need To Go Deeper

Documentation failed us, let’s go read the artifact. We listed the exact contents of the SPK in order:

$ tar -tvf dist/IceBoxExpress.spk

drwxr-xr-x  root/root     0  ./
-rw-r--r--  root/root  45874706  ./package.tgz
drwxr-xr-x  root/root     0  ./conf/
-rw-r--r--  root/root    62  ./conf/resource
-rw-r--r--  root/root    36  ./conf/privilege
-rw-r--r--  root/root    90  ./conf/protocol_file.json
drwxr-xr-x  root/root     0  ./WIZARD_UIFILES/
-rw-r--r--  root/root  1270  ./WIZARD_UIFILES/install_uifile
-rw-r--r--  root/root   428  ./INFO          <-- 9th entry
drwxr-xr-x  root/root     0  ./scripts/
...

Two things jumped out.

First, every entry had a ./ prefix. Kinda weird. What if the parser isn’t set up to treat them the same way?

Second, and this was a real hail mary by Claude, was that INFO was the ninth entry in the archive. It appeared after the . directory entry, a 45-megabyte package.tgz blob, three configuration files, and the wizard UI directory. The only thing the package parser actually needs is that INFO file, so maybe at some point it just gave up?

Both problems traced back to the same tar invocation:

tar --format=ustar -cf dist/IceBoxExpress.spk -C build/spk_root .

The root cause was that trailing .. When you tell tar to archive ., it walks the directory tree starting from the current directory entry. The resulting paths all carry the ./ prefix, and the order of entries is determined by filesystem inode allocation, which is random for all intents and purposes.

Success!

We replaced the glob with an explicit file list:

tar --format=ustar -cf dist/IceBoxExpress.spk -C build/spk_root \
    INFO package.tgz scripts conf WIZARD_UIFILES \
    LICENSE PACKAGE_ICON.PNG PACKAGE_ICON_256.PNG

Then we rebuilt, transferred to the NAS, uploaded to Package Center aaaaaaand for the first time, the installation wizard appeared!

Full disclosure: The first draft for this post was written by Claude Opus 4.6, which was then heavily rewritten, reframed, and edited by hand