Database Query Error with Dibi in PHP Script

I am having a script written like this:

<?php
date_default_timezone_set('Europe/Prague');

// Start time recording
$time_start = microtime(true);

include('./_includes.php');
$db = db_connector::connect();
if (!$db) {
    die('Database connection failed.');
}

// Instantiate audit object
$audit = new Audit(Audit::$udalost_typ[1]);

// Function to fetch data from DB using Dibi with error handling
function fetchFromDB($query, $params = []) {
    global $db;
    try {
        $result = $db->query($query, ...$params)->fetch();
        if (!$result) {
            throw new Exception('Query failed.');
        }
        return $result;
    } catch (Exception $e) {
        throw new Exception('Database query error: ' . $e->getMessage());
    }
}

try {
    // Retrieve JSON input
    $data = json_decode(file_get_contents('php://input'), true);

    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new Exception('Invalid JSON input.');
    }

    // Validate input data
    $requiredFields = ['uuid', 'task_id', 'typ_pozadavku', 'tel_cislo', 'datum'];
    $missingFields = array_filter($requiredFields, fn($field) => empty($data[$field]));

    if (!empty($missingFields)) {
        throw new Exception('Missing fields: ' . implode(', ', $missingFields));
    }

    // Extract and sanitize data
    $uuid = $data['uuid'];
    $task_id = (int)$data['task_id'];
    $typ_pozadavku = $data['typ_pozadavku'];
    $tel_cislo = $data['tel_cislo'];
    $datum = $data['datum'];

    // Begin transaction
    $db->begin();

    // Fetch org_id from device table
    $device = fetchFromDB('SELECT org_id, id, active FROM device WHERE uuid = %s', [$uuid]);
    if (!$device->active) {
        throw new Exception(ERROR_MSG['user_inactive']);
    }
    $org_id = (int)$device->org_id;

    // Fetch szif_task_id from task table
    $task = fetchFromDB(
        'SELECT szif_task_id FROM task WHERE org_id = %i AND id = %i',
        [$org_id, $task_id]
    );
    if (!$task) {
        throw new Exception('Task not found.');
    }
    $szif_task_id = (int)$task->szif_task_id;

    // Validate typ_pozadavku and phone number
    $valid_types = ['0', '1', '2'];
    if (!in_array($typ_pozadavku, $valid_types, true)) {
        throw new Exception(ERROR_MSG['input_validation_error']);
    }
    if (!validate_phonenumber($tel_cislo)) {
        throw new Exception(ERROR_MSG['input_validation_error']);
    }

    // Fetch last request and determine if another request can be sent
    $last_request = fetchFromDB(
        'SELECT last_time_sent, number_sent FROM contact_requests WHERE org_id = %i AND task_id = %i ORDER BY sent_at DESC LIMIT 1',
        [$org_id, $task_id]
    );

    $now = new DateTime();
    $can_send = true;

    if ($last_request) {
        $last_time_sent = new DateTime($last_request->last_time_sent);
        $interval = $now->diff($last_time_sent);

        if (($interval->h < 1 && $last_request->number_sent >= 2) || $interval->days < 1) {
            $can_send = false;
        }
    }

    if (!$can_send) {
        throw new Exception(ERROR_MSG['sync_needed']);
    }

    // Prepare and send IS_MACH request
    $ismach_data = [
        'ji' => $org_id,
        'task_id' => $szif_task_id,
        'typ_pozadavku' => $typ_pozadavku,
        'tel_cislo' => $tel_cislo,
        'datum' => $datum
    ];

    $ch = curl_init('http://localhost/ws/ismach/test_rozhrani.php');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($ismach_data));

    $ismach_result = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($http_code !== 200 || $curl_error) {
        throw new Exception('IS_MACH Request Failed: ' . $curl_error);
    }

    // Check IS_MACH Response
    $ismach_response = json_decode($ismach_result, true);
    if (json_last_error() !== JSON_ERROR_NONE || $ismach_response['status'] !== 'success') {
        throw new Exception('IS_MACH response error: ' . ($ismach_response['message'] ?? 'Unknown error'));
    }

    // Determine platform from audit logs
    $platform = 'web';
    $audit_log = fetchFromDB('SELECT poznamka FROM audit WHERE uuid = %s ORDER BY created DESC LIMIT 1', [$uuid]);
    if ($audit_log) {
        $note = $audit_log->poznamka;
        $platform = (strpos($note, 'android') !== false) ? 'Android' : ((strpos($note, 'iOS') !== false) ? 'iOS' : 'web');
    }

    $query = 'INSERT INTO contact_requests 
    (org_id, task_id, typ_pozadavku, tel_cislo, datum, last_time_sent, number_sent, platform_of_request, uuid, request_status, request_note, szif_task_id) 
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';

$params = [
    $org_id, $task_id, $typ_pozadavku, $tel_cislo, $datum,
    $now->format('Y-m-d H:i:s'),
    ($last_request ? $last_request->number_sent + 1 : 1),
    $platform, $uuid, 'ok', 'Request successfully sent to IS_MACH', $szif_task_id
];

    $db->query($query, $params);

    // Commit transaction
    $db->commit();

    // Log success in audit
    $audit->set_dulezitost(Audit::$udalost_dulezitost[0]);
    $audit->execute('Interface - success', json_encode($data), $ismach_result);

    echo json_encode(['status' => 'ok', 'message' => 'Request successful']);

} catch (Exception $e) {
    // Rollback transaction
    $db->rollback();

    // Log error in audit
    $audit->set_dulezitost(Audit::$udalost_dulezitost[1]);
    $audit->execute('Interface - error', json_encode($data), $e->getMessage());

    // Return error response
    echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}

// End time recording and log execution time
$time_end = microtime(true);
$audit->set_exec_time($time_end - $time_start);
?>

It throws an error like:

Array
(
    [status] => error
    [message] => Database query error: Query failed.
)
{"status":"error","message":"Database query error: Query failed."}

But I really don’t know why so…