AfterSave Hook Causing 500 Error - Even When Empty

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Juantreses
    Junior Member
    • Aug 2025
    • 9

    #1

    AfterSave Hook Causing 500 Error - Even When Empty

    This forum post may be deleted. I trusted my co-workers code but just went to check the Espo repo and just saw they used the complete wrong method signature for the afterSave

    Problem Summary


    I'm experiencing a 500 error when saving Leads, and the issue persists even with an almost completely empty AfterSave hook. The error doesn't appear in the EspoCRM logs, making it very difficult to debug.

    Background
    • Initially had ParallelError from Spatie Async library (which I assumed had something to do with the AfterSaveHook making HTTP calls)
    • Removed all webhook functionality to isolate the issue
    • 500 error persists even with minimal hook code
    • No error messages in EspoCRM logs (which might be worse than having errors)
    Current Hook Code
    This minimal hook still causes 500 errors on Lead save:

    PHP Code:
    <?php
    namespace Espo\Custom\Hooks\Lead;
    use 
    Espo\Core\Hook\Hook\AfterSave;
    use 
    Espo\ORM\Entity;
    use 
    Espo\ORM\EntityManager;
    use 
    Espo\Core\Utils\Log;
    use 
    Espo\Core\Utils\Config;
    use 
    Espo\Custom\Traits\WebhookTrait;
    class 
    AfterSaveHook implements AfterSave
    {
        
    //use WebhookTrait;
        /** @var string[] */
        
    private const FIELDS_TO_WATCH = [
            
    'cVankoCRM',
            
    'firstName',
            
    'lastName',
            
    'emailAddress',
            
    'phoneNumber',
            
    'cDateOfBirth',
            
    'cTeam',
        ];
        public function 
    __construct(
            
    //private Config $config,
            
    private readonly Log $log,
            
    //private readonly EntityManager $entityManager
        
    ) {}
        public function 
    afterSave(Entity $lead, array $options, array $data): void
        
    {
            try {
                
    //$this->log->info('Lead After Save Hook triggered for Lead ID: ' . $lead->getId());
              
                
    $isChanged false;
                foreach (
    self::FIELDS_TO_WATCH as $field) {
                    
    /*if ($lead->isAttributeChanged($field)) {
                        $isChanged = true;
                        break;
                    }*/
                
    }
              
                if (
    $isChanged) {
                    
    /*$leadData = [
                        'crm_id' => $lead->getId(),
                        'contact_id' => $lead->get('cVankoCRM'),
                        'first_name' => $lead->get('firstName'),
                        'last_name' => $lead->get('lastName'),
                        'email' => $lead->get('emailAddress'),
                        'phone' => $lead->get('phoneNumber'),
                        'date_of_birth' => $lead->get('cDateOfBirth'),
                    ];*/
                    //$this->sendVankoWebhook($leadData);
                
    }
            } catch (
    \Exception $e) {
                
    $this->log->error('Lead After Save Hook error: ' $e->getMessage());
            }
        }
        private function 
    sendVankoWebhook(array $leadData): void
        
    {
            
    //$endpoint = $this->config->get('vanko.webhooks.lead.process');
          
            
    if (!$endpoint) {
                
    //$this->log->warning('Vanko webhook endpoint not configured');
                
    return;
            }
            
    /*$this->queueWebhook(
                endpoint: $endpoint,
                payload: $leadData,
                serviceName: 'Vanko',
                method: 'POST',
                timeout: 30
            );*/
        
    }
        
    // Required by WebhookTrait
        /*protected function getEntityManager(): EntityManager
        {
            return $this->entityManager;
        }
        protected function getLog(): Log
        {
            return $this->log;
        }*/
    }
    Even reducing it to the absolute minimum which does nothing but log something:

    PHP Code:
    <?php
    namespace Espo\Custom\Hooks\Lead;
    use 
    Espo\Core\Hook\Hook\AfterSave;
    use 
    Espo\ORM\Entity;
    class 
    AfterSaveHook implements AfterSave
    {
        public function 
    afterSave(Entity $lead, array $options, array $data): void
        
    {
            
    $GLOBALS['log']->info("Minimal hook executed");
        }
    }
    What I've Tried
    1. ✅ Removed all HTTP/cURL calls from the hook
    2. ✅ Commented out all functional logic
    3. ✅ Removed WebhookTrait usage
    4. ✅ Cleared cache and rebuilt
    Questions
    1. Why would an empty AfterSave hook cause 500 errors?
    2. How can I debug this when nothing appears in EspoCRM logs?
    3. Are there specific requirements for AfterSave hook constructors that I'm missing?
    4. Could there be namespace/autoloading issues?
    Environment
    • EspoCRM version: 9.1.8
    • PHP version: 8.4.11
    Additional Notes
    • Lead saves work perfectly when the hook file is removed
    Last edited by Juantreses; Yesterday, 10:07 AM.
  • yuri
    EspoCRM product developer
    • Mar 2014
    • 9351

    #2
    It looks like it's an LLM generated code with programming language level mistakes. The error is likely not logged because invalid PHP code could not get compiled.

    The class does not correctly implement the interface afterSave. The method has the different signature.
    Last edited by yuri; Yesterday, 10:25 AM.
    If you find EspoCRM good, we would greatly appreciate if you could give the project a star on GitHub. We believe our work truly deserves more recognition. Thanks.

    Comment

    • yuri
      EspoCRM product developer
      • Mar 2014
      • 9351

      #3
      You can remove 'implements AfterSave'. And remove 'array $data' from the method.
      If you find EspoCRM good, we would greatly appreciate if you could give the project a star on GitHub. We believe our work truly deserves more recognition. Thanks.

      Comment

      • Juantreses
        Junior Member
        • Aug 2025
        • 9

        #4
        Yup, like I said in the edit you can remove it. Should not have trusted my co-workers code. It was indeed generated by an LLM. I did not dubbleCheck the afterSave method signature in the repository.

        Comment

        Working...