appwrite/appwrite · error · Appwrite\Extend\Exception

storage_file_type_unsupported

storage_file_type_unsupported

Error message

storage_file_type_unsupported

What it means

Returned synchronously (HTTP 400, code storage_file_type_unsupported) when the uploaded code file's extension is not in the allowed gzip set. The endpoint builds a FileExt validator restricted to FileExt::TYPE_GZIP, so only gzip-family file names (e.g. .tar.gz, .tgz, .gz) pass; a .zip or extension-less file is rejected before size checks.

Source

Thrown at src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php:161

        }

        $functionSizeLimit = (int) System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000');

        if (isset($plan['deploymentSize'])) {
            $functionSizeLimit = $plan['deploymentSize'] * 1000 * 1000;
        }

        $fileExt = new FileExt([FileExt::TYPE_GZIP]);
        $fileSizeValidator = new FileSize($functionSizeLimit);
        $upload = new Upload();

        // Make sure we handle a single file and multiple files the same way
        $fileName = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
        $fileTmpName = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
        $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];

        if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
            throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
        }

        $contentRange = $request->getHeaderLine('content-range');
        $deploymentId = ID::unique();
        $chunk = 1;
        $chunks = 1;

        if (!empty($contentRange)) {
            $start = $request->getContentRangeStart();
            $end = $request->getContentRangeEnd();
            $fileSize = $request->getContentRangeSize();
            $deploymentId = $request->getHeaderLine('x-appwrite-id', $deploymentId);
            // TODO make `end >= $fileSize` in next breaking version
            if (is_null($start) || is_null($end) || is_null($fileSize) || $end > $fileSize) {
                throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
            }

            $chunks = (int) ceil($fileSize / APP_LIMIT_UPLOAD_CHUNK_SIZE);

View on GitHub (pinned to 0cefd06153)

Solutions

  1. Package the deployment as a gzipped tarball: tar -czf code.tar.gz .
  2. Name the file with a gzip extension (.tar.gz / .tgz / .gz) in the multipart part
  3. If you have a zip, convert: gunzip is not needed — unzip then tar -czf
  4. Verify the SDK InputFile keeps the file name with extension

Example fix

# before: zip archive
zip -r code.zip . && curl -F 'code=@code.zip' ...

# after: gzip tarball
tar -czf code.tar.gz . && curl -F 'code=@code.tar.gz' -F 'entrypoint=index.js' -F 'activate=true' ...
Defensive patterns

Strategy: validation

Validate before calling

const GZIP_EXT = /\.(tar\.gz|tgz|gz)$/i;
if (!GZIP_EXT.test(fileName)) throw new Error('package the deployment as a gzip tarball (.tar.gz)');
form.append('code', file, 'code.tar.gz');

Type guard

const isGzipArchive = (name) => /\.(tar\.gz|tgz|gz)$/i.test(name);

Try / catch

try { await functions.createDeployment(functionId, file); } catch (e) { if (e.code === 'storage_file_type_unsupported') { /* repackage as tar.gz and retry */ } else throw e; }

Prevention

When it happens

Trigger: POST /v1/functions/:id/deployments with a file named code.zip, archive.zip, or any name whose extension is not a gzip type; also when the multipart filename loses its extension through SDK misconfiguration.

Common situations: Developers zipping code with zip instead of tar+gzip, CI pipelines producing .zip artifacts, filenames mangled by proxies, or the multipart filename set to a blob without extension.

Related errors


AI-assisted analysis of appwrite/appwrite@0cefd06153 (2026-08-18). Data as JSON: /api/errors/eeebe4781a3fe9b3. Report an issue: GitHub.