Cloudinary Laravel: Fix Upload Error & Configure
Fix 'Call to undefined method Cloudinary::upload()' in Laravel. Step-by-step guide to configure Cloudinary Laravel package, import correct facade, set up config, and upload files using facade or Storage disk.
How should I configure Cloudinary and use it in Laravel? I’m trying to implement Cloudinary in my Laravel project but I get the following error when calling the upload() method:
Call to undefined method Cloudinary\Cloudinary::upload()
How can I fix this? I installed the package with composer and imported the facade, but it seems to resolve to Cloudinary\Cloudinary. Below are my installation steps, controller code and config files.
Installed the package:
composer require cloudinary-labs/cloudinary-laravel
Facade import:
use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;
Controller code:
use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;
public function images(Request $request)
{
$request->validate([
'file' => 'required|image|max:1024',
]);
$result = Cloudinary::upload($request->file('file')->getRealPath())->getSecurePath();
dd($result->getPublicId());
}
cloudinary.php config:
return [
'notification_url' => env('CLOUDINARY_NOTIFICATION_URL'),
'cloud_url' => env('CLOUDINARY_URL', 'cloudinary://' . env('CLOUDINARY_KEY') . ':' . env('CLOUDINARY_SECRET') . '@' . env('CLOUDINARY_CLOUD_NAME')),
'cloud' => [
'cloud_name' => env('CLOUDINARY_CLOUD_NAME'),
'api_key' => env('CLOUDINARY_API_KEY'),
'api_secret' => env('CLOUDINARY_API_SECRET'),
],
];
filesystems.php config (relevant parts):
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app/private'),
'serve' => true,
'throw' => false,
'report' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
'throw' => false,
'report' => false,
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
'report' => false,
],
'cloudinary' => [
'driver' => 'cloudinary',
'key' => env('CLOUDINARY_KEY'),
'secret' => env('CLOUDINARY_SECRET'),
'cloud' => env('CLOUDINARY_CLOUD_NAME'),
'url' => env('CLOUDINARY_URL'),
'secure' => (bool) env('CLOUDINARY_SECURE', true),
],
],
Questions:
- Why does calling Cloudinary::upload(…) throw ‘Call to undefined method Cloudinary\Cloudinary::upload()’?
- Am I importing the correct facade or class?
- What is the correct way to configure Cloudinary in Laravel and upload files from a controller?
Cloudinary Laravel throws that error because your code is resolving to the SDK core class (Cloudinary\Cloudinary) instead of the package facade that exposes upload(); make sure you import and use the facade CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary, publish the package config and set CLOUDINARY_URL or the API key/secret environment variables, then clear caches. Once the facade (or the cloudinary filesystem disk) is wired correctly you can upload with Cloudinary::upload(...) or Storage::disk('cloudinary')->putFile(...) and read public_id / secure_url from the returned result.
Contents
- Why this error happens (Cloudinary Laravel)
- Check imports and alias collisions
- Configure Cloudinary in Laravel (Laravel Cloudinary configuration)
- Upload examples: Facade and Storage (Cloudinary upload)
- Troubleshooting checklist and quick tests
- Sources
- Conclusion
Why this error happens (Cloudinary Laravel)
That error — Call to undefined method Cloudinary\Cloudinary::upload() — means PHP ended up calling a method on the SDK core class, Cloudinary\Cloudinary, which doesn’t implement an upload() helper. The Cloudinary Laravel package exposes convenient helpers through its facade CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary; if your code or an alias points to Cloudinary\Cloudinary instead, upload() won’t exist there.
Why does that happen? A few common causes:
- You imported or aliased the wrong class (for example
use Cloudinary\Cloudinary;or a bareuse Cloudinary;). - A manual alias in
config/app.phpmapsCloudinaryto the SDK class instead of the package facade. - Package auto-discovery or service provider registration failed (so the facade binding isn’t registered).
- Cache/autoload still references an old binding (after changing imports/config).
The package README explains the proper facade usage and the blog post on Cloudinary’s site highlights the same root cause and fix (import the facade rather than the core SDK) — see the GitHub README and Cloudinary’s Laravel guide for confirmation and examples. For a community discussion of similar issues see a relevant StackOverflow thread.
Check imports and aliases
Quick checks you can do right away:
- Confirm your controller imports the facade exactly:
use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;
Do NOT have any competing imports like use Cloudinary\Cloudinary; or use Cloudinary;.
- If you prefer not to import, call the facade by its fully-qualified name to be explicit:
\CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::upload($path);
- Look in config/app.php (only if you manually added an alias). The alias should point to the facade, not SDK:
'aliases' => [
// ...
'Cloudinary' => \CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::class,
],
- Detect what the facade is resolving to (run this in a route/controller temporarily):
dd(get_class(\CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::getFacadeRoot()));
If that prints Cloudinary\Cloudinary, you found the problem — the binding points to the SDK core.
For background, the package README contains the canonical import and usage examples: https://github.com/cloudinary-community/cloudinary-laravel. The Cloudinary blog also documents the common mis-import issue: https://cloudinary.com/blog/laravel_file_upload_to_a_local_server_or_to_the_cloud.
Configure Cloudinary in Laravel (Laravel Cloudinary configuration)
Follow these steps to make sure Laravel is configured correctly.
- Install (you already did):
composer require cloudinary-labs/cloudinary-laravel
- Publish the package config (if needed):
php artisan vendor:publish --provider="CloudinaryLabs\CloudinaryLaravel\CloudinaryServiceProvider"
This drops config/cloudinary.php you can edit.
- Provide credentials in your .env (pick one approach). Recommended: use CLOUDINARY_URL:
CLOUDINARY_URL=cloudinary://<API_KEY>:<API_SECRET>@<CLOUD_NAME>
Or define each var:
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
Important: be consistent. Your config/cloudinary.php and config/filesystems.php should read the same env names. In your posted cloudinary.php you mix CLOUDINARY_KEY/CLOUDINARY_SECRET with CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET — unify those names so the package can authenticate.
- Filesystem disk (example in
config/filesystems.php):
'disks' => [
// ...
'cloudinary' => [
'driver' => 'cloudinary',
'url' => env('CLOUDINARY_URL'),
'secure' => (bool) env('CLOUDINARY_SECURE', true),
],
],
(See the package README for the exact disk keys your installed version expects.)
- Clear config cache so Laravel reads the new env entries:
php artisan config:clear composer dump-autoload
If you want the authoritative config examples and provider details, check the package repo and Cloudinary’s guide: https://github.com/cloudinary-community/cloudinary-laravel and https://cloudinary.com/blog/laravel_file_upload_to_a_local_server_or_to_the_cloud.
Upload examples: Facade and Storage (Cloudinary upload)
Below are safe examples you can paste into your controller. They handle the different return shapes that older/newer versions may produce.
A. Using the package facade (explicit import at top of file: use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary;)
public function images(Request $request)
{
$request->validate(['file' => 'required|image|max:1024']);
$path = $request->file('file')->getRealPath();
$upload = Cloudinary::upload($path, ['folder' => 'products']);
// The package/SDK may return an array or an object depending on version.
if (is_array($upload)) {
$publicId = $upload['public_id'] ?? null;
$secureUrl = $upload['secure_url'] ?? null;
} else {
$publicId = method_exists($upload, 'getPublicId') ? $upload->getPublicId() : ($upload->public_id ?? null);
$secureUrl = method_exists($upload, 'getSecurePath') ? $upload->getSecurePath() : ($upload->secure_url ?? null);
}
return response()->json(['public_id' => $publicId, 'secure_url' => $secureUrl]);
}
Note: your original chain did ->getSecurePath() and then ->getPublicId() — that sets $result to a string (secure URL) and then attempts to call a method on a string. Don’t do that; inspect the upload result first.
B. Using Laravel Filesystem (recommended if you want to treat Cloudinary like a disk)
use Illuminate\Support\Facades\Storage;
public function images(Request $request)
{
$request->validate(['file' => 'required|image|max:1024']);
$result = Storage::disk('cloudinary')->putFile('products', $request->file('file'));
// $result may be a path string or an array depending on driver version.
return response()->json(['upload' => $result]);
}
C. Alternative: storeOnCloudinary (macro added by some integrations)
$info = $request->file('file')->storeOnCloudinary('products');
// inspect $info to see public_id / secure_url
A community step-by-step example that demonstrates the storeOnCloudinary approach can be helpful: https://dev.to/issaahmed/laravel-image-management-with-cloudinary-a-step-by-step-guide-to-uploading-updating-and-deleting-images-57nl.
Troubleshooting checklist and quick tests
If uploads still fail, go through this checklist in order:
-
- Confirm the facade resolves to the package wrapper:
-
Add
dd(get_class(\CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary::getFacadeRoot()));— it should NOT beCloudinary\Cloudinary. -
- Remove conflicting imports/aliases:
-
Search codebase for
use Cloudinary\Cloudinary;or anyCloudinaryalias inconfig/app.phpand remove/replace it. -
- Re-publish config and clear caches:
php artisan vendor:publish --provider="CloudinaryLabs\CloudinaryLaravel\CloudinaryServiceProvider"
php artisan config:clear
php artisan cache:clear
composer dump-autoload
-
- Validate env variables:
-
Prefer
CLOUDINARY_URLor keepCLOUDINARY_CLOUD_NAME/CLOUDINARY_API_KEY/CLOUDINARY_API_SECRETconsistent with your published config. -
- Check installed packages and versions:
composer show cloudinary-labs/cloudinary-laravel composer show cloudinary/cloudinary_php
Older/very new versions may change return shapes or method names — consult the package README on GitHub.
-
- If the facade still resolves to the SDK class, inspect the service container:
dd(app()->bound('cloudinary'), app('cloudinary'));
That helps identify what the service provider bound.
-
- As a fallback use the SDK directly (if you want tight control):
$cloudinary = new \Cloudinary\Cloudinary(env('CLOUDINARY_URL'));
$response = $cloudinary->uploadApi()->upload($path, ['folder' => 'products']);
(Only use this if you understand the SDK API; the package facade is simpler for most apps.)
See community troubleshooting examples on StackOverflow for similar import/binding mistakes: https://stackoverflow.com/questions/77136480/how-can-i-config-properly-cloudinary-in-laravel-10.
Sources
- GitHub - cloudinary-community/cloudinary-laravel: Laravel SDK for Cloudinary
- Laravel Cloudinary Integration: Optimization & CDN Delivery (WPWebInfotech)
- How Can I config properly cloudinary in laravel 10? (Stack Overflow)
- Laravel File Upload to a Local Server Or to the Cloud (Cloudinary blog)
- Laravel Image Management with Cloudinary (DEV)
- ERROR: Call to undefined method CloudinaryLabs\CloudinaryLaravel\CloudinaryAdapter::getPathPrefix() · Issue #107 · cloudinary-community/cloudinary-laravel
- Some help with Cloudinary upload through Laravel (Laracasts discussion)
- Cloudinary and Rails: undefined method `upload’ for nil:NilClass (Stack Overflow)
Conclusion
Fixing this is usually straightforward: make sure Laravel resolves to the package facade (use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary), publish and align the cloudinary config with your .env (or set CLOUDINARY_URL), clear caches, and then upload using either Cloudinary::upload(...) or Storage::disk('cloudinary')->putFile(...). Once the facade/binding is correct you’ll stop seeing Call to undefined method Cloudinary\Cloudinary::upload() and your Cloudinary Laravel uploads will return public_id and secure_url you can store or serve.