How to fix StorageApiError: Invalid key in Supabase

SupabaseBEGINNERMEDIUMUpdated Dec 29, 2025· 231 views

This error occurs when attempting to upload a file or access an object in Supabase Storage with an invalid file path. Common causes include special characters in the file name, empty path strings, non-ASCII characters that get URL-encoded, or structural issues in the path format.

Quick fix

Fixes StorageApiError: Invalid key

// ❌ Wrong - leading slash
const { data, error } = await supabase.storage
  .from('bucket')
  .upload('/path/to/file.png', file);

// ✅ Correct - no leading slash
const { data, error } = await supabase.storage
  .from('bucket')
  .upload('path/to/file.png', file);

if (error) {
  console.error('Upload error:', error.message);
}
  1. Verify the file path does not start with a leading slash
  2. Check for empty or null file path
  3. Remove or replace special characters in file names
  4. Sanitize file names with non-ASCII characters
supabase — error
// ❌ Wrong - leading slash
const { data, error } = await supabase.storage
.from('bucket')
.upload('/path/to/file.png', file);
// ✅ Correct - no leading slash
const { data, error } = await supabase.storage
.from('bucket')
.upload('path/to/file.png', file);
if (error) {
console.error('Upload error:', error.message);
}
StorageApiError: Invalid key

What this error means

The "Invalid key" error is thrown by Supabase Storage API when the file path or key you're using doesn't conform to S3 safe character requirements. Supabase Storage validates file paths using a regex pattern that enforces AWS S3 object key naming rules. The validation function checks if your key contains only allowed characters—most special characters like `%`, `#`, `?`, and some encoded sequences are rejected. This is different from an authentication or API key error; it's specifically about the file path you're trying to upload or access. Understanding what constitutes a valid file path is essential for working with Supabase Storage reliably.

Typical symptoms

  • File upload fails with "Invalid key" error message
  • Error occurs when uploading files with non-ASCII characters in filename
  • Storage API returns 400 status with "Invalid key" error
  • Empty file path string results in "Invalid key: " error
  • File names containing special characters (%, #, ?) are rejected
  • URL-encoded file names trigger the error despite being valid in other contexts
  • Uploading the same file works in Supabase UI but fails programmatically

Common causes

  1. File path contains percent-encoded characters from URL encoding (e.g., %20 for space)
  2. File name includes non-ASCII characters (Chinese, Japanese, emoji) that get encoded
  3. Empty or null file path string passed to upload() or download() methods
  4. Path contains special characters not allowed by S3 specification: %, #, ?, or certain control characters
  5. File path starts with a leading slash (/path/to/file instead of path/to/file)
  6. Double slashes in path (path//to/file creates invalid key)
  7. File name or path exceeds S3 object key length limits (1024 bytes)
  8. Incorrect character encoding of file path before sending to Storage API

How to fix "StorageApiError: Invalid key"

1Verify the file path does not start with a leading slash

Supabase Storage expects relative paths without leading slashes. Check your upload code and remove any leading / character:

typescript
// ❌ Wrong - leading slash
const { data, error } = await supabase.storage
  .from('bucket')
  .upload('/path/to/file.png', file);

// ✅ Correct - no leading slash
const { data, error } = await supabase.storage
  .from('bucket')
  .upload('path/to/file.png', file);

if (error) {
  console.error('Upload error:', error.message);
}

Paths should always be relative to the bucket root, not absolute file system paths.

2Check for empty or null file path

Ensure you're passing a non-empty string as the file path. Empty strings will trigger "Invalid key: " error:

typescript
// ❌ Wrong - empty path
const filePath = '';
const { data, error } = await supabase.storage
  .from('bucket')
  .upload(filePath, file);
// Error: Invalid key:

// ✅ Correct - provide a valid file path
const filePath = 'documents/my-file.pdf';
const { data, error } = await supabase.storage
  .from('bucket')
  .upload(filePath, file);

if (error) {
  console.error('Error:', error.message);
}

Add validation to ensure the path is always defined before uploading.

3Remove or replace special characters in file names

The following characters are not allowed in Supabase Storage paths and must be removed or replaced:

- % (percent signs from URL encoding)
- # (hash/fragment)
- ? (question mark/query)
- Control characters (ASCII 0-31)
- Certain high Unicode characters that get encoded

typescript
// ❌ Wrong - contains special characters
const fileName = 'file#1.png';           // Contains #
const fileName = '100%done.pdf';        // Contains %
const fileName = 'what?.docx';          // Contains ?

// ✅ Correct - safe characters only
const fileName = 'file-1.png';          // Replace # with -
const fileName = '100-done.pdf';        // Replace % with -
const fileName = 'what.docx';           // Remove ?

// Safe function to sanitize file names
function sanitizeFileName(fileName: string): string {
  return fileName
    .replace(/[#%?]/g, '-')              // Replace invalid chars with dash
    .replace(/\s+/g, '_')               // Replace spaces with underscore
    .replace(/[^a-zA-Z0-9._\-/]/g, ''); // Remove other unsafe chars
}

const safePath = sanitizeFileName(originalFileName);
const { data, error } = await supabase.storage
  .from('bucket')
  .upload(safePath, file);

Always sanitize user-provided file names before uploading.

4Sanitize file names with non-ASCII characters

File names containing non-ASCII characters (Chinese, Japanese, emoji, accents) get URL-encoded by the browser and can result in invalid key errors. The best approach is to use a UUID or sanitized name:

typescript
import { v4 as uuid } from 'uuid';

// ❌ Problematic - non-ASCII characters
const fileName = '文件.pdf';  // Chinese characters
const filePath = `uploads/${fileName}`;
// Gets encoded to %E6%96%87%E4%BB%B6.pdf which may fail

// ✅ Best approach - use UUID
const fileExtension = originalFile.name.split('.').pop();
const safeFileName = `${uuid()}.${fileExtension}`;
const filePath = `uploads/${safeFileName}`;

const { data, error } = await supabase.storage
  .from('bucket')
  .upload(filePath, file);

if (error) {
  console.error('Upload error:', error.message);
}

// Alternative - sanitize and preserve original name in metadata
function sanitizeFileName(fileName: string): string {
  return fileName
    .replace(/[^a-zA-Z0-9._-]/g, '_')  // Replace non-ASCII with underscore
    .toLowerCase()
    .substring(0, 255);                  // Limit length
}

const originalName = originalFile.name;
const safeName = sanitizeFileName(originalName);
const filePath = `uploads/${safeName}`;

const { data, error } = await supabase.storage
  .from('bucket')
  .upload(filePath, file, {
    metadata: {
      originalFileName: originalName
    }
  });

Using UUIDs is the most reliable approach for user-uploaded files.

5Avoid double slashes and fix path structure

Ensure your file paths don't contain double slashes and are properly formatted:

typescript
// ❌ Wrong - double slash
const filePath = 'uploads//my-file.pdf';      // Double slash
const filePath = 'uploads/./file.pdf';        // Unnecessary dot
const filePath = 'uploads/../file.pdf';       // Relative path traversal

// ✅ Correct - clean paths
const filePath = 'uploads/my-file.pdf';
const filePath = 'uploads/subfolder/file.pdf';

// Safe path builder function
function buildFilePath(...segments: string[]): string {
  return segments
    .filter(s => s && s.length > 0)      // Remove empty segments
    .join('/')
    .replace(/\/+/g, '/')               // Remove double slashes
    .replace(/^\//, '');                // Remove leading slash
}

const filePath = buildFilePath('uploads', userId, fileName);
const { data, error } = await supabase.storage
  .from('bucket')
  .upload(filePath, file);

Always validate the path structure before sending to the API.

6Check file path length and validate before upload

S3 object keys have a maximum length of 1024 bytes. Create a validation function:

typescript
function validateFilePath(filePath: string): { valid: boolean; error?: string } {
  // Check not empty
  if (!filePath || filePath.trim().length === 0) {
    return { valid: false, error: 'File path cannot be empty' };
  }

  // Check not too long
  if (filePath.length > 1024) {
    return { valid: false, error: 'File path exceeds 1024 bytes' };
  }

  // Check no leading slash
  if (filePath.startsWith('/')) {
    return { valid: false, error: 'File path cannot start with /' };
  }

  // Check no double slashes
  if (filePath.includes('//')) {
    return { valid: false, error: 'File path cannot contain //' };
  }

  // Check for invalid characters
  const invalidChars = ['%', '#', '?'];
  for (const char of invalidChars) {
    if (filePath.includes(char)) {
      return { valid: false, error: `File path cannot contain '${char}' character` };
    }
  }

  return { valid: true };
}

// Usage
const validation = validateFilePath(filePath);
if (!validation.valid) {
  console.error('Invalid file path:', validation.error);
  return;
}

const { data, error } = await supabase.storage
  .from('bucket')
  .upload(filePath, file);

Validating before attempting upload saves time and provides better error messages.

7Verify bucket name and ensure proper client initialization

While "Invalid key" typically refers to file path issues, also verify your Supabase client and bucket name are correct:

typescript
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

// Verify bucket exists and you have access
const { data: buckets, error: listError } = await supabase.storage.listBuckets();

if (listError) {
  console.error('Cannot list buckets:', listError.message);
} else {
  const bucketNames = buckets.map(b => b.name);
  console.log('Available buckets:', bucketNames);
  
  if (!bucketNames.includes('your-bucket')) {
    console.error('Bucket "your-bucket" not found');
  }
}

// Then attempt upload
const { data, error } = await supabase.storage
  .from('your-bucket')
  .upload('valid/path/file.png', file);

if (error) {
  console.error('Upload error:', error.message);
}

Ensure your Supabase URL and key are correctly set from environment variables.

Advanced notes

S3 Object Key Naming Rules: Supabase Storage strictly enforces AWS S3 object key naming conventions. Keys can contain 1-1024 bytes and can only use characters from the set: a-z, A-Z, 0-9, !, -, _, ., *, ', (, ), and /. Any other characters, including percent-encoded sequences like %20, will cause validation to fail.

URL Encoding Confusion: A common issue occurs when developers URL-encode file names thinking it will help. For example, a space becomes %20, but Supabase Storage API rejects the % character itself. Always pass clean, unencoded paths to the Supabase SDK—the SDK handles encoding if needed.

Non-ASCII Character Handling: JavaScript strings can contain any Unicode character, but when you pass them to Supabase Storage, they get URL-encoded for HTTP transmission. Supabase then validates the encoded string, which may contain % characters that violate the key rules. This is why using UUIDs or sanitized ASCII-only names is the most reliable approach.

Client Library Versions: Older versions of the Supabase JavaScript client may have different error messages or validation behavior. Ensure you're using a recent version: npm update @supabase/supabase-js.

Error Message Format: The API sometimes returns "Invalid key: " with a trailing space when the path is completely empty. If you see this exact message, check if your path variable is an empty string or null.

Self-Hosted Storage Validation: If running self-hosted Supabase with the storage service, the validation rules may differ slightly between versions. Check your storage service logs for more detailed error information: docker logs supabase-storage.

Recovery: Once you receive an "Invalid key" error, the request has already been rejected before attempting to write to S3. There's no partial upload or cleanup needed—simply fix the path and retry.

Frequently asked questions

What does "StorageApiError: Invalid key" mean?
The "Invalid key" error is thrown by Supabase Storage API when the file path or key you're using doesn't conform to S3 safe character requirements. Supabase Storage validates file paths using a regex pattern that enforces AWS S3 object key naming rules. , and some encoded sequences are rejected.
How do I fix "StorageApiError: Invalid key"?
This error occurs when attempting to upload a file or access an object in Supabase Storage with an invalid file path. Common causes include special characters in the file name, empty path strings, non-ASCII characters that get URL-encoded, or structural issues in the path format.
What causes "StorageApiError: Invalid key"?
File path contains percent-encoded characters from URL encoding (e.g., %20 for space) File name includes non-ASCII characters (Chinese, Japanese, emoji) that get encoded Empty or null file path string passed to upload() or download() methods
Is the "StorageApiError: Invalid key" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading