mcpField
B
bitrix-result-and-errors

bitrix-result-and-errors

github.com/bxmaximumDevelopment47279 installsUpdated 2w ago
Repository

About bitrix-result-and-errors

A Bitrix development skill for consistent result and error handling. It explains when to return Result objects or raise exceptions, along with error codes, ORM results, and controller boundaries.

Key features

  1. 01Rules for Result versus exceptions
  2. 02Stable machine-readable error codes
  3. 03Typed result patterns
  4. 04ORM result handling guidance
  5. 05Controller error response patterns
  6. 06Exception handling at module boundaries

Use cases

  1. 01Designing service method contracts
  2. 02Returning validation and business errors
  3. 03Handling failed ORM operations
  4. 04Mapping exceptions to controller errors

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bxmaximum/bitrix-framework-skills --skill bitrix-result-and-errors

After installing, type/bitrix-result-and-errorsin Claude Code to confirm it loaded.

Details

From the project README

Result and Error

Baseline: main 23.0+ · Verified: main 26.800.0

Contract

  • Expected failures (validation, business rule, not found, no rights) → Result with Errors.
  • Broken state or contract (module missing, DB down, misconfiguration, bug) → exception.
  • One method, one contract: always a Result, or a value plus exceptions for the exceptional case. Never bool/null/-1/['error' => …].

Bitrix\Main\Result

use Bitrix\Main\Error;
use Bitrix\Main\Result;

$result = new Result();
if ($title === '')
{
    return $result->addError(new Error('Title required', 'BLOG_POST_TITLE_EMPTY', ['field' => 'title']));
}

return $result->setData(['id' => $id]);
  • isSuccess(), addError(Error), addErrors(Error[]), getErrors(), getError() (first or null), getErrorMessages(), getErrorCollection(), setData(array), getData(). Setters return $this.
  • setData() silently drops SqlExpression values.

Error and codes

new Error($message, $code = 0, $customData = null). JSON form: {"message", "code", "customData"}.

  • Codes are stable and machine-readable: module + entity + reason (BLOG_POST_NOT_FOUND), kept in constants. Clients branch on the code, not the text.
  • Messages via Loc::getMessage('BLOG_ERROR_POST_TITLE_EMPTY').
  • ErrorCollection: new ErrorCollection([$error]), setError(), add([...]), getErrorByCode('CODE'), iterable, isEmpty(), toArray().

Typed result

Give callers getters instead of getData()['post']:

final class CreatePostResult extends \Bitrix\Main\Result
{
    private ?Post $post = null;

    public function setPost(Post $post): self
    {
        $this->post = $post;
        return $this;
    }

    public function getPost(): ?Post
    {
        return $this->post;
    }
}

ORM results (Bitrix\Main\ORM\Data\*)

  • AddResult: getId(), getPrimary(), getObject().
  • UpdateResult: getId(), getPrimary(), getAffectedRowsCount().
  • DeleteResult: no methods of its own (no affected-rows count).
  • Bitrix\Main\Entity\*Result are compat aliases. Write ORM\Data\*.
  • A failed ORM result whose errors nobody read (isSuccess() / getErrors()) raises E_USER_WARNING on destruct. Always check it and return it, or copy its errors up.
$add = PostTable::add(['TITLE' => $title]);
if (!$add->isSuccess())
{
    return (new Result())->addErrors($add->getErrors());
}
$id = $add->getId();

Other result types

  • EventResult (SUCCESS, ERROR, UNDEFINED) is a handler's answer, not a service result: bitrix-events.
  • ValidationResult (bitrix-validation) extends Result: add its errors to the service result.

Controllers

$this->addErrors($result->getErrors()); return null; gives {"status":"error","data":null,"errors":[…]}. Envelope details: bitrix-controllers.

Exceptions at the boundary

Catch at the module/controller edge, log, return a generic error:

try
{
    return $this->service->run($dto);
}
catch (\Bitrix\Main\SystemException $e)
{
    $this->logger->error('Run failed', ['exception' => $e]);
    return (new Result())->addError(new Error('Internal error', 'VENDOR_INTERNAL_ERROR'));
}
  • Never swallow \Throwable without logging.
  • Developer mistakes (InvalidArgumentException, LogicException, TypeError) and "can't happen" domain states (division by zero in a money type) stay exceptions.

Comments

Markdown supported