ArrayValue: strlen/substr instead of mb_strlen/mb_substr — multibyte import failures

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • dimyy
    Active Community Member
    • Jun 2018
    • 602

    #1

    ArrayValue: strlen/substr instead of mb_strlen/mb_substr — multibyte import failures

    Environment
    • EspoCRM 9.x
    • PostgreSQL (client_encoding = UTF-8)
    • PHP 8.3
    Symptom


    When importing a CSV (or otherwise saving) an entity with an array attribute whose values contain multibyte UTF-8 characters (Cyrillic, em-dash —, etc.) and whose byte length exceeds the array_value.value max length (default 100), the import fails with:

    SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near ","
    LINE 1: ...entity_type") VALUES ('69f867d4...', false, , 'painPoints', ...
    ^

    …followed by the cascading:

    SQLSTATE[25P02]: In failed sql transaction: current transaction is aborted, commands ignored until end of transaction block

    The same input file fails in different rows on each retry — the failure looks unstable, but it is fully deterministic once you look at the data byte-by-byte. Root cause


    application/Espo/Repositories/ArrayValue.php, inside storeEntityAttribute():


    phpif (strlen($value) > $itemMaxLength) {
    $value = substr($value, 0, $itemMaxLength);
    }

    strlen() and substr() are byte-level. For a UTF-8 string consisting mostly of 2-byte characters (Cyrillic) plus an occasional 3-byte one (—), cutting at byte 100 frequently lands in the middle of a multibyte sequence, producing an invalid UTF-8 string.


  • yuri
    EspoCRM product developer
    • Mar 2014
    • 9814

    #2
    Will be fixed.

    I don't understand why the validation did not trigger the error when you imported. It should check items length.

    Comment

    • dimyy
      Active Community Member
      • Jun 2018
      • 602

      #3
      I didn’t dig into it deeply — just applied a monkey patch, and everything works fine.
      If anything, here’s the error log:

      [2026-05-04 09:35:36] ERROR: Import: SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near ","
      LINE 1: ...entity_type") VALUES ('69f8686854b911183', false, , 'painPoi...
      ^
      [2026-05-04 09:35:36] ERROR: Import: SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block

      [2026-05-04 09:35:36] CRITICAL: (25P02) SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block :: POST /Import :: /home/work/public_html/application/Espo/ORM/Executor/DefaultSqlExecutor.php(77)
      [object] (PDOException(code: 25P02): SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block at /home/work/public_html/application/Espo/ORM/Executor/DefaultSqlExecutor.php:77)
      [stacktrace]
      #0 /home/work/public_html/application/Espo/ORM/Executor/DefaultSqlExecutor.php(77): PDO->query()
      #1 /home/work/public_html/application/Espo/ORM/Executor/DefaultSqlExecutor.php(69): Espo\ORM\Executor\DefaultSqlExecutor->executeSqlWithDeadlockHandling()
      #2 /home/work/public_html/application/Espo/ORM/Executor/DefaultQueryExecutor.php(48): Espo\ORM\Executor\DefaultSqlExecutor->execute()
      #3 /home/work/public_html/application/Espo/ORM/Mapper/BaseMapper.php(1484): Espo\ORM\Executor\DefaultQueryExecutor->execute()
      #4 /home/work/public_html/application/Espo/ORM/Repository/RDBRepository.php(159): Espo\ORM\Mapper\BaseMapper->update()
      #5 /home/work/public_html/application/Espo/Core/Repositories/Database.php(144): Espo\ORM\Repository\RDBRepository->save()
      #6 /home/work/public_html/application/Espo/ORM/EntityManager.php(253): Espo\Core\Repositories\Database->save()
      #7 /home/work/public_html/application/Espo/Tools/Import/Import.php(387): Espo\ORM\EntityManager->saveEntity()
      #8 /home/work/public_html/application/Espo/Tools/Import/Service.php(99): Espo\Tools\Import\Import->run()
      #9 /home/work/public_html/application/Espo/Tools/Import/Api/Post.php(76): Espo\Tools\Import\Service->import()
      #10 /home/work/public_html/application/Espo/Core/Api/ActionHandler.php(73): Espo\Tools\Import\Api\Post->process()
      #11 /home/work/public_html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): Espo\Core\Api\ActionHandler->handle()
      #12 /home/work/public_html/application/Espo/Core/Api/RouteProcessor.php(176): Slim\MiddlewareDispatcher->handle()
      #13 /home/work/public_html/application/Espo/Core/Api/RouteProcessor.php(145): Espo\Core\Api\RouteProcessor->processAction()
      #14 /home/work/public_html/application/Espo/Core/Api/RouteProcessor.php(126): Espo\Core\Api\RouteProcessor->processAfterAuth()
      #15 /home/work/public_html/application/Espo/Core/Api/RouteProcessor.php(78): Espo\Core\Api\RouteProcessor->processInternal()
      #16 /home/work/public_html/application/Espo/Core/Api/Starter.php(126): Espo\Core\Api\RouteProcessor->process()
      #17 /home/work/public_html/vendor/slim/slim/Slim/Handlers/Strategies/RequestResponse.php(38): Espo\Core\Api\Starter->{closure:Espo\Core\Api\Starter::addRoute():117} ()
      #18 /home/work/public_html/vendor/slim/slim/Slim/Routing/Route.php(363): Slim\Handlers\Strategies\RequestResponse->__invoke()
      #19 /home/work/public_html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): Slim\Routing\Route->handle()
      #20 /home/work/public_html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): Slim\MiddlewareDispatcher->handle()
      #21 /home/work/public_html/vendor/slim/slim/Slim/Routing/Route.php(321): Slim\MiddlewareDispatcher->handle()
      Unminify assets or how to recreate t...o.min.js file? /home/work/public_html/vendor/slim/slim/Slim/Routing/RouteRunner.php(74): Slim\Routing\Route->run()
      Unminify assets or how to recreate t...o.min.js file? /home/work/public_html/vendor/slim/slim/Slim/Middleware/RoutingMiddleware.php(45): Slim\Routing\RouteRunner->handle()
      #24 /home/work/public_html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(129): Slim\Middleware\RoutingMiddleware->process()
      Unminify assets or how to recreate t...o.min.js file? /home/work/public_html/vendor/slim/slim/Slim/Middleware/ErrorMiddleware.php(77): Psr\Http\Server\RequestHandlerInterface@anonymous->handle()
      how to upgrade ? /home/work/public_html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(129): Slim\Middleware\ErrorMiddleware->process()
      how to upgrade ? /home/work/public_html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): Psr\Http\Server\RequestHandlerInterface@anonymous->handle()
      #28 /home/work/public_html/vendor/slim/slim/Slim/App.php(209): Slim\MiddlewareDispatcher->handle()
      #29 /home/work/public_html/vendor/slim/slim/Slim/App.php(193): Slim\App->handle()
      #30 /home/work/public_html/application/Espo/Core/Api/Starter.php(84): Slim\App->run()
      #31 /home/work/public_html/application/Espo/Core/ApplicationRunners/Api.php(45): Espo\Core\Api\Starter->start()
      Unminify assets or how to recreate t...o.min.js file? /home/work/public_html/application/Espo/Core/Application/RunnerRunner.php(84): Espo\Core\ApplicationRunners\Api->run()
      Quotes /home/work/public_html/application/Espo/Core/Application.php(78): Espo\Core\Application\RunnerRunner->run()
      Quotes /home/work/public_html/public/api/v1/index.php(35): Espo\Core\Application->run()
      #35 {main}

      It also seems strange to me that when importing multiple times in a row after hitting 500, I click “Import” and get different results — all attempts are recorded in the import results, but they fail on different records each time; the behavior is non-deterministic.

      Comment

      Working...