-
Notifications
You must be signed in to change notification settings - Fork 983
Open
Description
Description
The NCAT API uploads files to cloud storage without specifying Content-Type headers across
all storage providers (S3-compatible and Google Cloud Storage). This causes upload failures
with Supabase Storage and prevents inline media viewing in browsers.
Environment
- NCAT Version:
stephengpope/no-code-architects-toolkit:latest - Build Number: 206
- Python Version: 3.9.23
- Deployment: Docker
- Tested Storage Backends:
- Supabase Storage (S3-compatible) ❌ Fails with InvalidMimeType
- AWS S3
⚠️ Defaults to application/octet-stream - DigitalOcean Spaces
⚠️ Defaults to application/octet-stream - Google Cloud Storage
⚠️ Auto-detects (unreliable)
Problem Details
Error from production logs:
ERROR:services.s3_toolkit:Error uploading file to S3: An error occurred (InvalidMimeType)
when calling the PutObject operation: mime type application/octet-stream is not supported
Failure Statistics (Oct 15-21, 2025):
- 42 unique job failures over 7 days
- 41 failures today alone (accelerating issue)
- Affected endpoints:
/v1/image/convert/video- 41 failures (98%)/v1/media/download- 1 failure (2%)
User Impact:
- ❌ Supabase Storage rejects uploads entirely
- ❌ MP4 videos download instead of playing in browser
- ❌ MP3 audio files download instead of streaming
- ❌ Images download instead of displaying inline
Root Cause
1. S3-Compatible Provider (services/s3_toolkit.py)
def upload_to_s3(file_path, s3_url, access_key, secret_key, bucket_name, region):
# ...
client.upload_fileobj(data, bucket_name,
os.path.basename(file_path),
ExtraArgs={'ACL': 'public-read'}) # ❌ No ContentType
Problem: boto3 defaults to application/octet-stream when ContentType is not specified.
2. Google Cloud Storage Provider (services/gcp_toolkit.py)
def upload_to_gcs(file_path, bucket_name=GCP_BUCKET_NAME):
# ...
blob = bucket.blob(os.path.basename(file_path))
blob.upload_from_filename(file_path) # ❌ No content_type parameter
Problem: GCP auto-detects MIME type, but this is unreliable.
Proposed Solution
Use Python's built-in mimetypes library to auto-detect Content-Type from file extensions:
Fix for services/s3_toolkit.py
import mimetypes
def upload_to_s3(file_path, s3_url, access_key, secret_key, bucket_name, region):
session = boto3.Session(
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
region_name=region
)
client = session.client('s3', endpoint_url=s3_url)
try:
# Auto-detect MIME type from file extension
content_type, _ = mimetypes.guess_type(file_path)
if not content_type:
content_type = 'application/octet-stream'
logger.info(f"Uploading {os.path.basename(file_path)} with Content-Type:
{content_type}")
with open(file_path, 'rb') as data:
client.upload_fileobj(data, bucket_name,
os.path.basename(file_path),
ExtraArgs={
'ACL': 'public-read',
'ContentType': content_type # ✅ Add this
})
encoded_filename = quote(os.path.basename(file_path))
file_url = f"{s3_url}/{bucket_name}/{encoded_filename}"
return file_url
except Exception as e:
logger.error(f"Error uploading file to S3: {e}")
raise
Fix for services/gcp_toolkit.py
import mimetypes
def upload_to_gcs(file_path, bucket_name=GCP_BUCKET_NAME):
# ... existing validation ...
# Auto-detect MIME type from file extension
content_type, _ = mimetypes.guess_type(file_path)
if not content_type:
content_type = 'application/octet-stream'
logger.info(f"Uploading {os.path.basename(file_path)} with Content-Type: {content_type}")
bucket = gcs_client.bucket(bucket_name)
blob = bucket.blob(os.path.basename(file_path))
blob.upload_from_filename(file_path, content_type=content_type) # ✅ Add content_type
# ... rest of function ...
Benefits
✅ Eliminates InvalidMimeType errors with Supabase Storage✅ Enables inline viewing of media
files in browsers✅ Consistent behavior across all storage backends✅ Zero breaking changes -
fully backwards compatible✅ No new dependencies - uses Python standard library
Additional Notes
I'm happy to submit a PR with both fixes if this approach is acceptable. This issue affects all
users regardless of which storage backend they choose.Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels