Версії для прикладів у «Типові помилки Anchor та їх вирішення» перевірено 2 серпня 2026 року. Стабільна гілка Anchor v1 має релізи 1.0.x і орієнтується на Solana 3.x; Anchor v2 у документації позначений як alpha. Приклади для Anchor 0.29–0.32 залишаються лише відтворюваними прикладами для зафіксованого legacy-середовища: їх не слід переносити в новий проєкт без міграції залежностей і повторного тестування.

Робота з Anchor неминуче супроводжується помилками компіляції, виконання, тестування та розгортання. Цей матеріал систематизує найпоширеніші з них, пояснює причини й дає конкретні кроки для перевірки та усунення. Усі приклади перевірені для Anchor 0.29.0, Solana CLI 1.18.x, кластер — Devnet.

Помилки компіляції

Макроси Anchor: невірне використання

Найчастіша категорія помилок на старті. Макроси Anchor (`#[account]`, `#[derive(Accounts)]`, `#[instruction(...)]`) мають суворі правила застосування, і компілятор Rust видає повідомлення, яке важко інтерпретувати без розуміння внутрішньої логіки фреймворку.

Типова помилка: використання `#[account]` на структурі без поля `pub` або без правильних атрибутів.

Неправильний варіант:

#[account]
struct MyData {
  value: u64,
}

Компілятор повідомить про відсутність реалізації трейту `AccountSerialize` або `Owner`. Причина — `#[account]` генерує код, який очікує, що структура містить принаймні одне поле з модифікатором `pub`, а також правильно вказаний `owner` (якщо це не автоматичне визначення через `#[account(mut)]` у контексті інструкції).

Правильний варіант:

#[account]
pub struct MyData {
  pub value: u64,
}

Ще один випадок: макрос `#[instruction(...)]` використано з неправильними аргументами. Цей макрос дозволяє передавати в constraint-и значення з аргументів інструкції. Якщо ви вказали ім'я аргументу, якого немає у сигнатурі функції, компілятор видасть помилку `unresolved import` або `cannot find value`.

Перевірка: порівняйте імена у `#[instruction(my_arg)]` з параметрами функції інструкції. Вони мають збігатися точно, включно з регістром.

Типові Rust-помилки у Anchor-контексті

Anchor генерує значну кількість коду за макросами, тому стандартні помилки Rust набувають специфічного забарвлення.

`borrow of moved value` — виникає, коли ви намагаєтеся використати один і той самий акаунт двічі в контексті інструкції без клонування. Наприклад, передати `ctx.accounts.authority` і в constraint, і в CPI-виклик.

Рішення — клонувати `Pubkey` перед використанням:

let authority_key = ctx.accounts.authority.key();
// тепер authority_key можна використовувати скільки завгодно разів

`mismatched types` при роботі з `Clock` — часта ситуація, коли розробник порівнює `i64` (Unix timestamp) з `u64` (слот). Перевірте типи через `ctx.accounts.clock.unix_timestamp` (це `i64`) і приведіть типи явно: `unix_timestamp as u64` або навпаки.

Помилки виконання (runtime)

ConstraintViolation — розбір кожного constraint

Коли інструкція викликається, Anchor перевіряє всі constraint-и, задані в структурі контексту. Якщо хоча б один не виконується, транзакція відхиляється з помилкою `ConstraintViolation`. Код помилки містить номер constraint-у, але без розуміння порядку їхнього оголошення це малоінформативно.

Нижче — таблиця найпоширеніших constraint-ів із поясненням причини відхилення:

Constraint Що перевіряє Типова причина відхилення
mut Акаунт передано як записуваний У транзакції акаунт не позначено як writable
signer Акаунт підписав транзакцію Відсутній підпис у транзакції або підписав не той акаунт
seeds = [...] PDA (Program Derived Address) збігається з переданим акаунтом Неправильні сиди, помилка в порядку сидів або додатковий bump не збігається
has_one = authority Поле `authority` акаунта збігається з ключем `authority` з контексту Владелець акаунта не збігається з переданим authority
owner = *system_program Власник акаунта — вказана програма Акаунт належить іншій програмі або ще не ініціалізовано
constraint = ... Довільна умова, що повертає bool Логічна умова обчислилась як false
init Акаунт буде створено (має бути порожнім) Акаунт вже ініціалізовано (містить дані)
space = ... Розмір акаунта достатній для даних Вказано замалий розмір, дані не вміщуються

Практичний приклад діагностики: якщо ви отримали `ConstraintViolation` з кодом, але не розумієте, який саме constraint спрацював, додайте тимчасово `msg!` перед кожним constraint-блоком у тесті або перевірте порядок оголошення полів у структурі — Anchor нумерує constraint-и саме в порядку оголошення полів.

Важливе обмеження: у Devnet іноді трапляється ситуація, коли PDA-акаунт існує, але його дані пошкоджені через попередні невдалі розгортання. У такому разі `init` може відхилитися, хоча акаунт здається порожнім. Перевірте стан акаунта через `solana account <pubkey>` — якщо `data` не порожній, акаунт треба закрити та створити заново.</pubkey>

InstructionFallbackNotFound

Ця помилка виникає, коли клієнт надсилає інструкцію з ідентифікатором, якого немає у програмі. Anchor генерує диспетчер інструкцій на основі `#[program]`-блоку, і якщо жоден метод не збігається з переданим 8-байтовим ідентифікатором, програма повертає цю помилку.

Типові причини:

  • Клієнт використовує старий IDL, у якому ще немає нового методу.
  • Ви деплойнули одну версію програми, а тест використовує іншу (наприклад, після перекомпіляції забули повторити `anchor build`).
  • Ручне формування інструкції з неправильним селектором.

Перевірка: порівняйте хеш-селектор у транзакції з очікуваним. У Anchor селектор — це перші 8 байтів SHA-256 від рядка `global:<method_name>`. У тестовому середовищі найпростіше переконатися, що `anchor build` виконано після кожної зміни коду.</method_name>

Помилки тестування

Тест не знаходить програму

Симптом: `Error: Account not found` або `Error: Program <address> not found` під час виконання `anchor test`.</address>

Причина 1: програма не зібрана. Перед запуском тестів обов'язково виконайте `anchor build`. Anchor тестує локально на Localnet, і програма має бути скомпільована у директорії `target/deploy/`.

Причина 2: неспівпадіння Program ID. Перевірте, що `Anchor.toml` містить правильний Program ID, і що цей самий ID вказано у файлі `lib.rs` у атрибуті `#[program]` (через `declare_id!`). Якщо ці ID різні, тестовий фреймворк не зможе знайти програму за адресою.

Кроки перевірки:

  1. Виконайте `anchor build`.
  2. Перевірте вміст `target/deploy/` — там має бути файл `<program_name>.so` та `<program_name>-keypair.json`.</program_name></program_name>
  3. Порівняйте публічний ключ з keypair-файла зі значенням у `Anchor.toml` (розділ `[programs.localnet]`).

Акаунт не ініціалізовано перед використанням

Симптом: `AccountNotInitialized` або `ConstraintViolation` при спробі прочитати або змінити акаунт, який очікується як існуючий.

Концептуальне пояснення: у Anchor є два способи роботи з акаунтами — `init` (створення) і без `init` (використання існуючого). Якщо у контексті інструкції акаунт оголошено без `init`, але в тесті ви не створили його раніше, програма спробує десеріалізувати порожні дані і зазнає невдачі.

Неправильний підхід у тесті:

// Акаунт dataAccount оголошено без init у контексті
await program.methods.someMethod().accounts({ dataAccount: dataAccountPubkey }).rpc();

Правильний підхід — спочатку викликати інструкцію, що створює акаунт (з `init` у контексті), і лише потім використовувати його:

// Крок 1: ініціалізація
await program.methods.initialize().accounts({ dataAccount: dataAccountPubkey, payer: payer.publicKey }).rpc();

// Крок 2: використання
await program.methods.someMethod().accounts({ dataAccount: dataAccountPubkey }).rpc();

Важливо розуміти: це не обмеження фреймворку, а фундаментальна властивість моделі акаунтів Solana — кожен акаунт має бути явно створений і оплачений перед використанням.

Помилки розгортання

Program ID conflict

Симптом: під час `anchor deploy` з'являється помилка про те, що Program ID у ключі не збігається з оголошеним у програмі.

Причина: Anchor генерує keypair для програми при першому `anchor build` і записує публічний ключ у `target/deploy/<program>-keypair.json`. Цей ключ має збігатися з тим, що вказано через `declare_id!` у `lib.rs` та в `Anchor.toml`. Якщо ви вручну змінили один із цих файлів без оновлення інших, виникає конфлікт.</program>

Кроки усунення:

  1. Відкрийте `target/deploy/<program>-keypair.json` і отримайте публічний ключ (через `solana-keygen pubkey <file>`).</file></program>
  2. Переконайтеся, що цей ключ вказано у `declare_id!` у `lib.rs`.
  3. Переконайтеся, що цей ключ вказано в `Anchor.toml` у розділі `[programs.devnet]`.
  4. Якщо ви хочете використати новий ключ — видаліть старий keypair-файл і виконайте `anchor build` заново. Anchor згенерує нову пару.

Безпечний відкат: якщо ви випадково задеплоїли програму з неправильним ID, на Devnet це не несе ризиків втрати коштів (програма просто не буде працювати коректно). Але якщо ви плануєте оновлювати цю програму надалі, важливо негайно виправити конфлікт, щоб не накопичувати невідповідностей.

Buffer не оновлено

Симптом: після `anchor deploy` програма на Devnet працює зі старою логікою, хоча код змінено.

Причина: Anchor використовує проміжний Buffer-акаунт для розгортання. Якщо `anchor build` не було виконано перед деплоєм, або якщо білд завершився з помилками (які ви могли не помітити), Buffer містить стару версію байт-коду.

Кроки перевірки:

  1. Виконайте `anchor build` і переконайтеся, що білд завершився без помилок (немає `error[E...]` у виводі).
  2. Перевірте розмір файлу `target/deploy/<program>.so` — він має змінитися після зміни коду.</program>
  3. Виконайте `anchor deploy` і зверніть увагу на вивід — Anchor показує, який Buffer використовується.

Додаткова перевірка: після розгортання викличте `solana program show <program_id>` і порівняйте `data_len` з розміром вашого `.so`-файлу. Якщо вони різні — розгортання не відбувся повністю.</program_id>

Як діагностувати

anchor test --verbose

Звичайний `anchor test` показує лише остаточний результат: пройшов тест чи ні. Прапорець `--verbose` додає детальний вивід компіляції, розгортання та логи транзакцій.

Що дає --verbose:

  • Повний вивід `cargo build-sbf` — ви бачите всі попередження та помилки компіляції Rust.
  • Логи кожної транзакції в тесті, включно з log-повідомленнями програми (вивід `msg!`).
  • Точний код помилки та акаунт, на якому вона виникла.

Команда для запуску:

anchor test --verbose

Якщо потрібно ще більше деталей про конкретну транзакцію, додайте `--` після тестів і вкажіть фільтр:

anchor test --verbose -- test_name

Обмеження: `--verbose` не показує внутрішні кроки виконання інструкції (trace). Для цього потрібні інструменти рівня SVM, які виходять за межі цього матеріалу.

Читання логів через solana logs

Для діагностики на Devnet (не в локальних тестах) використовується `solana logs`. Ця команда підключається до валідатора і показує поточні логи транзакцій у реальному часі.

Середовище: Devnet, Solana CLI 1.18.x.

Команда:

solana logs --url devnet

Щоб фільтрувати логи лише за вашою програмою, додайте фільтр за Program ID:

solana logs --url devnet --filter <program_id></program_id>

Що шукати в логах:

  • Рядки `Program log:` — це вивід ваших `msg!` викликів. Якщо ви бачите `msg!` перед помилкою, але не бачите після — помилка виникла між цими точками.
  • Рядки `Program <id> invoke [1]` та `Program <id> success` — показують вхід і вихід з програми. Якщо є `invoke` без `success` — програма завершилась з помилкою.</id></id>
  • Рядки `Program <id> failed: custom program error: 0x...` — hex-код помилки. У Anchor цей код відповідає номеру помилки з `error!` макросу у вашому коді.</id>

Практична порада: перед діагностикою на Devnet додайте `msg!` виклики на початок і кінець кожної інструкції. Це дозволить локалізувати місце помилки навіть без детального trace. Після діагностики видаліть зайві `msg!` — кожен лог-виклик споживає compute-одиниці.

Наступний логічний крок: після того, як ви навчилися діагностувати та усувати типові помилки Anchor, варто ознайомитися з інструментами базового налаштування середовища — зокрема, з тим, як встановити та налаштувати Solana CLI для стабільної роботи.

Джерела