How to Fix “Error Uploading File” in PHP When No PHP Error Is Displayed

I’ve been there building a file upload feature in PHP, selecting a file, hitting upload, and boom “Sorry, there was an error uploading your file.” No PHP error logs, nothing in the browser console, and the file never shows up in the directory. Frustrating, Let me walk you through what caused this, how I fixed it, and how you can improve your own upload code to make it more reliable and user-friendly.

My Original Code

Here’s the initial code I used:

if(isset($_POST["btn-vd-submit"]) AND $vd_perm_actual > 0) {
    $filename = $_FILES['vdfile']['name'];
    $target_dir = "./voice-demo-files/";
    $target_file = $target_dir . basename($_FILES['vdfile']['name']);
    $uploadOk = 1;
    $vdFileType = pathinfo($target_file,PATHINFO_EXTENSION);

    if (file_exists($target_file)) {
        echo "Sorry, file already exists.";
        $uploadOk = 0;
    }

    if ($_FILES['vdfile']['size'] > 50000000000) {
        echo "Sorry, your file is too large.";
        $uploadOk = 0;
    }

    if($vdFileType != "mp3") {
        echo "Sorry, only mp3 files are allowed.";
        $uploadOk = 0;
    }

    if ($uploadOk == 0) {
        echo "Sorry, your file was not uploaded.";
    } else {
        if (move_uploaded_file($_FILES['vdfile']['tmp_name'], $target_file)) {
            echo "The file ". basename($_FILES['vdfile']['name']). " has been uploaded.";
        } else {
            echo "Sorry, there was an error uploading your file.";
        }
    }
}

And the HTML form:

<form class="custom-form" method="post" enctype="multipart/form-data">
  <div class="s-4 m-4 center">
    <center>Choose A Voice Demo File (.mp3) to upload and link with your profile:<br><br><br>
    <input type="file" name="vdfile" id="vdfile"><br><br><br>
  </div>
  <div class="s-4 m-4 center">
    <button class="submit-form center button background-primary text-white" name="btn-vd-submit" type="submit">
      Upload This Voice Demo!
    </button>
  </div>
</form>

At first glance, it looks fine, but it only gave me generic failure messages. I had no idea what was really wrong.

Why the File Upload Failed Without Showing Error

After some digging, I found the real issue:
I never checked $_FILES['vdfile']['error']. PHP sets error codes during upload (UPLOAD_ERR_*), but my code ignored them.

Here are common reasons this happens:

  • PHP upload errors:
    • UPLOAD_ERR_INI_SIZE → file exceeded upload_max_filesize in php.ini
    • UPLOAD_ERR_FORM_SIZE → file exceeded MAX_FILE_SIZE in the HTML form
    • UPLOAD_ERR_NO_FILE → no file was uploaded
    • UPLOAD_ERR_PARTIAL → file only partially uploaded
  • Server limits:
    • upload_max_filesize too small
    • post_max_size smaller than the file
    • file_uploads turned off
  • Directory problems:
    • ./voice-demo-files/ didn’t exist
    • Directory not writable by PHP
  • Insecure validation:
    • I trusted only the file extension instead of checking MIME type

That’s why move_uploaded_file() silently failed.

The Fixe and Improved Upload Code

Here’s the robust, production-ready version I now use:

<?php
ini_set('display_errors', 1);
ini_set('log_errors', 1);
error_reporting(E_ALL);

$errors = [];
$messages = [];
$vd_perm_actual = $vd_perm_actual ?? 1;

if (isset($_POST['btn-vd-submit']) && $vd_perm_actual > 0) {
    if (!isset($_FILES['vdfile'])) {
        $errors[] = 'No file field named vdfile was submitted.';
    } else {
        $file = $_FILES['vdfile'];

        $errorMap = [
            UPLOAD_ERR_OK         => 'Upload successful.',
            UPLOAD_ERR_INI_SIZE   => 'File exceeds upload_max_filesize in php.ini.',
            UPLOAD_ERR_FORM_SIZE  => 'File exceeds MAX_FILE_SIZE in the form.',
            UPLOAD_ERR_PARTIAL    => 'File was only partially uploaded.',
            UPLOAD_ERR_NO_FILE    => 'No file was uploaded.',
            UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder.',
            UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.',
            UPLOAD_ERR_EXTENSION  => 'A PHP extension stopped the upload.'
        ];

        if ($file['error'] !== UPLOAD_ERR_OK) {
            $errors[] = $errorMap[$file['error']] ?? 'Unknown upload error.';
        } else {
            $targetDir = __DIR__ . '/voice-demo-files/';
            if (!is_dir($targetDir) && !mkdir($targetDir, 0775, true)) {
                $errors[] = 'Upload directory missing and could not be created.';
            }
            if (!is_writable($targetDir)) {
                $errors[] = 'Upload directory is not writable.';
            }

            $maxBytes = 20 * 1024 * 1024; // 20 MB
            if ($file['size'] > $maxBytes) {
                $errors[] = 'File too large. Max allowed is 20 MB.';
            }

            $finfo = new finfo(FILEINFO_MIME_TYPE);
            $mime = $finfo->file($file['tmp_name']);
            $allowed = ['audio/mpeg' => 'mp3', 'audio/mp3' => 'mp3'];
            if (!isset($allowed[$mime])) {
                $errors[] = 'Invalid type. Only MP3 files allowed.';
            }

            $ext = $allowed[$mime] ?? 'bin';
            $safeBase = preg_replace('/[^A-Za-z0-9_\-]/', '_', pathinfo($file['name'], PATHINFO_FILENAME));
            $safeBase = $safeBase ?: 'upload';
            $destFilename = $safeBase . '__' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
            $destPath = $targetDir . $destFilename;

            if (empty($errors) && is_uploaded_file($file['tmp_name'])) {
                if (move_uploaded_file($file['tmp_name'], $destPath)) {
                    $messages[] = 'Upload completed: ' . htmlspecialchars($destFilename);
                } else {
                    $errors[] = 'Failed to move uploaded file. Check directory permissions.';
                }
            }
        }
    }
}
?>

This script tells me exactly why the upload failed, instead of just “error uploading file.”

Server Checklist

Before testing again, I also checked my server config:

  • In php.ini:
    • file_uploads = On
    • upload_max_filesize = 20M
    • post_max_size = 22M
    • max_file_uploads = 20
  • Directory setup: mkdir -p voice-demo-files chown www-data:www-data voice-demo-files chmod 775 voice-demo-files
  • On SELinux systems: chcon -t httpd_sys_rw_content_t voice-demo-files -R

Extra Practice Feature

Once my upload worked, I added some fun practice improvements:

  • Auto-rename on duplicate: append counters like (1), (2).
  • List uploaded files with delete links: $files = glob(__DIR__.'/voice-demo-files/*.mp3'); foreach ($files as $f) echo basename($f)."<br>";
  • Check both extension & MIME for extra safety.
  • Limit duration/bitrate using ffprobe if you want stricter audio validation.
  • Progress bar using JavaScript XMLHttpRequest.onprogress.

My Troubleshooting Flow

If you’re still stuck, here’s what I do:

  1. Upload a tiny MP3 (<100 KB).
  2. Print $_FILES['vdfile']['error'].
  3. Check PHP limits (upload_max_filesize, post_max_size).
  4. Verify directory path and permissions.
  5. Look at error_log for hidden PHP issues.

Final Thought

What I learned from this debugging session is simple: never trust generic error messages. Always inspect $_FILES['vdfile']['error'], validate the directory, and double-check PHP/server limits. Once I added clear error handling, not only did the problem disappear, but I also gained confidence that future uploads will fail safely and clearly when something goes wrong.

Related blog posts