troubleshoot-errors
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTroubleshoot Errors Skill
错误排查技能
Quick Triage Workflow
快速分类工作流
Step 1: Identify Error Category
步骤1:识别错误类别
- Compilation error → Check syntax, types, abilities
- Linker error → Check dependencies, named addresses
- Runtime abort (ABORTED) → Check error code, find failed assertion
- Object error → See Object-Related Errors section below (CRITICAL)
- Test error → Check assertions, expected failures
- Type error → Check generic types, type conversions
- 编译错误 → 检查语法、类型、ability
- 链接器错误 → 检查依赖、命名地址
- 运行时终止(ABORTED) → 检查错误码,定位失败的断言
- 对象错误 → 参见下方对象相关错误章节(关键)
- 测试错误 → 检查断言、预期失败项
- 类型错误 → 检查泛型类型、类型转换
Step 2: Top 10 Common Errors (Quick Fixes)
步骤2:10大常见错误(快速修复)
- "object does not exist" → Verify seed/creator address for named objects
- "RESOURCE_NOT_FOUND" → Add clause to function
acquires - "Type mismatch" → Use to convert address to Object
object::address_to_object<T>() - "Ability constraint not satisfied" → Add required ability (key, drop, copy, store)
- "unbound variable" → Declare variable before use or fix typo
- "missing acquires" → Add to function signature
acquires ResourceType - "Named address not found" → Add address to Move.toml section
[addresses] - "Package dependencies not resolved" → Add dependency to Move.toml
- "Expected semicolon" → Add semicolon at end of statement
- "ungated transfers disabled" → Use instead of
object::transfer_with_ref()object::transfer()
See for complete error database.
references/error-catalog.md- "object does not exist" → 验证命名对象的seed/创建者地址
- "RESOURCE_NOT_FOUND" → 为函数添加子句
acquires - "Type mismatch" → 使用将地址转换为Object
object::address_to_object<T>() - "Ability constraint not satisfied" → 添加所需的ability(key、drop、copy、store)
- "unbound variable" → 使用变量前先声明,或修正拼写错误
- "missing acquires" → 在函数签名中添加
acquires ResourceType - "Named address not found" → 在Move.toml的区块中添加对应地址
[addresses] - "Package dependencies not resolved" → 在Move.toml中添加对应依赖
- "Expected semicolon" → 在语句末尾添加分号
- "ungated transfers disabled" → 使用替代
object::transfer_with_ref()object::transfer()
完整错误库请查看。
references/error-catalog.mdObject-Related Errors ⭐ CRITICAL
对象相关错误 ⭐ 关键
These are the most common and complex errors in Aptos Move V2.
这类错误是Aptos Move V2中最常见也最复杂的错误。
Error: "An object does not exist at this address"
错误:"An object does not exist at this address"
Cause: Trying to access a resource at an object address that hasn't been created yet.
Common Scenarios:
原因: 尝试访问尚未创建的对象地址上的资源。
常见场景:
Scenario 1: Collection owner can't create tokens
场景1:集合所有者无法创建代币
move
// ❌ WRONG: Storing collection's extend_ref
fun init_module(deployer: &signer) {
let collection_ref = collection::create_unlimited_collection(...);
let collection_signer = object::generate_signer(&collection_ref);
move_to(&collection_signer, Config {
extend_ref: object::generate_extend_ref(&collection_ref), // Wrong ref!
});
}
public entry fun mint_nft() acquires Config {
let config = borrow_global<Config>(...);
let signer = object::generate_signer_for_extending(&config.extend_ref);
// ❌ ERROR: Collection can't create tokens in itself
token::create_named_token(&signer, ...);
}move
// ✅ CORRECT: Create marketplace object that OWNS the collection
fun init_module(deployer: &signer) {
// Create marketplace state object
let marketplace_ref = object::create_named_object(deployer, b"MARKETPLACE_STATE");
let marketplace_signer = object::generate_signer(&marketplace_ref);
// Marketplace creates collection (marketplace is the owner)
collection::create_unlimited_collection(&marketplace_signer, ...);
// Store MARKETPLACE object's extend_ref
move_to(&marketplace_signer, MarketplaceConfig {
extend_ref: object::generate_extend_ref(&marketplace_ref),
});
}
public entry fun mint_nft() acquires MarketplaceConfig {
let config = borrow_global<MarketplaceConfig>(...);
// Use marketplace signer (collection owner)
let marketplace_signer = object::generate_signer_for_extending(&config.extend_ref);
token::create_named_token(&marketplace_signer, ...); // ✅ Works!
}Key lesson: When minting tokens into a collection, use the collection owner's signer, not the collection's
signer.
move
// ❌ 错误:存储集合的extend_ref
fun init_module(deployer: &signer) {
let collection_ref = collection::create_unlimited_collection(...);
let collection_signer = object::generate_signer(&collection_ref);
move_to(&collection_signer, Config {
extend_ref: object::generate_extend_ref(&collection_ref), // 错误的ref!
});
}
public entry fun mint_nft() acquires Config {
let config = borrow_global<Config>(...);
let signer = object::generate_signer_for_extending(&config.extend_ref);
// ❌ 错误:集合无法在自身内部创建代币
token::create_named_token(&signer, ...);
}move
// ✅ 正确:创建拥有集合的市场对象
fun init_module(deployer: &signer) {
// 创建市场状态对象
let marketplace_ref = object::create_named_object(deployer, b"MARKETPLACE_STATE");
let marketplace_signer = object::generate_signer(&marketplace_ref);
// 市场创建集合(市场是所有者)
collection::create_unlimited_collection(&marketplace_signer, ...);
// 存储市场对象的extend_ref
move_to(&marketplace_signer, MarketplaceConfig {
extend_ref: object::generate_extend_ref(&marketplace_ref),
});
}
public entry fun mint_nft() acquires MarketplaceConfig {
let config = borrow_global<MarketplaceConfig>(...);
// 使用市场签名者(集合所有者)
let marketplace_signer = object::generate_signer_for_extending(&config.extend_ref);
token::create_named_token(&marketplace_signer, ...); // ✅ 运行正常!
}核心要点: 向集合中铸造代币时,使用集合所有者的签名者,而非集合本身的签名者。
Scenario 2: init_module never ran
场景2:init_module从未运行
move
// During deployment, init_module failed an assertion
fun init_module(deployer: &signer) {
assert!(signer::address_of(deployer) == @marketplace_addr, E_NOT_AUTHORIZED);
// If this fails, MarketplaceConfig is never created
move_to(deployer, MarketplaceConfig { ... });
}Fix:
- Verify resolves to correct address during deployment
@marketplace_addr - For object deployment, use not
aptos move deploy-objectpublish - Check deployment transaction succeeded
move
// 部署过程中init_module的断言失败
fun init_module(deployer: &signer) {
assert!(signer::address_of(deployer) == @marketplace_addr, E_NOT_AUTHORIZED);
// 如果此处失败,MarketplaceConfig将永远不会被创建
move_to(deployer, MarketplaceConfig { ... });
}修复方案:
- 部署时验证解析为正确地址
@marketplace_addr - 对象部署请使用而非
aptos move deploy-object命令publish - 检查部署交易是否成功执行
Scenario 3: Wrong address calculation
场景3:地址计算错误
move
// ❌ WRONG: Incorrect seed or creator address
let obj_addr = object::create_object_address(&wrong_creator, b"SEED");
let config = borrow_global<Config>(obj_addr); // Doesn't exist at this addressmove
// ✅ CORRECT: Use correct creator address and seed
let obj_addr = object::create_object_address(&@marketplace_addr, b"MARKETPLACE_STATE");
let config = borrow_global<MarketplaceConfig>(obj_addr);move
// ❌ 错误:seed或创建者地址不正确
let obj_addr = object::create_object_address(&wrong_creator, b"SEED");
let config = borrow_global<Config>(obj_addr); // 该地址不存在对应资源move
// ✅ 正确:使用正确的创建者地址和seed
let obj_addr = object::create_object_address(&@marketplace_addr, b"MARKETPLACE_STATE");
let config = borrow_global<MarketplaceConfig>(obj_addr);Error: "The object does not have ungated transfers enabled"
错误:"The object does not have ungated transfers enabled"
Cause: Trying to use on an object with disabled ungated transfers.
object::transfer()move
// ❌ WRONG
public entry fun mint_and_transfer(creator: &signer, recipient: address) {
let constructor_ref = token::create_named_token(...);
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
// Disable ungated transfers
object::disable_ungated_transfer(&transfer_ref);
let token_obj = object::object_from_constructor_ref<Token>(&constructor_ref);
// ❌ ERROR: ungated transfers are disabled!
object::transfer(creator, token_obj, recipient);
}move
// ✅ CORRECT
public entry fun mint_and_transfer(creator: &signer, recipient: address) {
let constructor_ref = token::create_named_token(...);
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
// Disable ungated transfers
object::disable_ungated_transfer(&transfer_ref);
// ✅ Use transfer_with_ref instead
let linear_transfer_ref = object::generate_linear_transfer_ref(&transfer_ref);
object::transfer_with_ref(linear_transfer_ref, recipient);
// Store transfer_ref for future transfers
let token_signer = object::generate_signer(&constructor_ref);
move_to(&token_signer, TokenData { transfer_ref, ... });
}Rule:
- called? → Use
object::disable_ungated_transfer()object::transfer_with_ref() - Ungated transfers enabled (default)? → Use
object::transfer()
原因: 尝试对已禁用无门槛转账的对象使用方法。
object::transfer()move
// ❌ 错误
public entry fun mint_and_transfer(creator: &signer, recipient: address) {
let constructor_ref = token::create_named_token(...);
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
// 禁用无门槛转账
object::disable_ungated_transfer(&transfer_ref);
let token_obj = object::object_from_constructor_ref<Token>(&constructor_ref);
// ❌ 错误:无门槛转账已被禁用!
object::transfer(creator, token_obj, recipient);
}move
// ✅ 正确
public entry fun mint_and_transfer(creator: &signer, recipient: address) {
let constructor_ref = token::create_named_token(...);
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
// 禁用无门槛转账
object::disable_ungated_transfer(&transfer_ref);
// ✅ 改用transfer_with_ref
let linear_transfer_ref = object::generate_linear_transfer_ref(&transfer_ref);
object::transfer_with_ref(linear_transfer_ref, recipient);
// 存储transfer_ref供后续转账使用
let token_signer = object::generate_signer(&constructor_ref);
move_to(&token_signer, TokenData { transfer_ref, ... });
}规则:
- 调用了?→ 使用
object::disable_ungated_transfer()object::transfer_with_ref() - 无门槛转账已启用(默认状态)?→ 使用
object::transfer()
Error: "Object address derivation mismatch"
错误:"Object address derivation mismatch"
Cause: Named object address doesn't match expected address.
move
// ❌ WRONG: Inconsistent seeds
let obj = object::create_named_object(creator, b"SEED_V1");
// Later, trying to access with different seed
let obj_addr = object::create_object_address(&creator_addr, b"SEED_V2"); // Wrong seed!move
// ✅ CORRECT: Use consistent seeds
const SEED: vector<u8> = b"MY_OBJECT_SEED";
// Creation
let obj = object::create_named_object(creator, SEED);
// Access
let obj_addr = object::create_object_address(&creator_addr, SEED);原因: 命名对象地址与预期地址不匹配。
move
// ❌ 错误:seed不一致
let obj = object::create_named_object(creator, b"SEED_V1");
// 后续尝试使用不同的seed访问
let obj_addr = object::create_object_address(&creator_addr, b"SEED_V2"); // 错误的seed!move
// ✅ 正确:使用统一的seed
const SEED: vector<u8> = b"MY_OBJECT_SEED";
// 创建对象
let obj = object::create_named_object(creator, SEED);
// 访问对象
let obj_addr = object::create_object_address(&creator_addr, SEED);Common Error Patterns
常见错误模式
Pattern 1: Object Access
模式1:对象访问
move
// ❌ WRONG
let item = borrow_global<Item>(item_obj); // Passing Object<Item>, need address
// ✅ CORRECT
let item_addr = object::object_address(&item_obj);
let item = borrow_global<Item>(item_addr);move
// ❌ 错误
let item = borrow_global<Item>(item_obj); // 传入的是Object<Item>,需要地址
// ✅ 正确
let item_addr = object::object_address(&item_obj);
let item = borrow_global<Item>(item_addr);Pattern 2: Missing acquires
模式2:缺少acquires子句
move
// ❌ WRONG
public fun get_balance(addr: address): u64 { // Missing 'acquires'
let account = borrow_global<Account>(addr);
account.balance
}
// ✅ CORRECT
public fun get_balance(addr: address): u64 acquires Account {
let account = borrow_global<Account>(addr);
account.balance
}move
// ❌ 错误
public fun get_balance(addr: address): u64 { // 缺少'acquires'
let account = borrow_global<Account>(addr);
account.balance
}
// ✅ 正确
public fun get_balance(addr: address): u64 acquires Account {
let account = borrow_global<Account>(addr);
account.balance
}Pattern 3: Incorrect Error Codes
模式3:错误码不规范
move
// ❌ WRONG
assert!(condition, 0); // Using 0 - unclear what failed
// ✅ CORRECT
const E_CONDITION_FAILED: u64 = 1;
assert!(condition, E_CONDITION_FAILED);move
// ❌ 错误
assert!(condition, 0); // 使用0作为错误码,无法明确失败原因
// ✅ 正确
const E_CONDITION_FAILED: u64 = 1;
assert!(condition, E_CONDITION_FAILED);Debugging Strategies
调试策略
1. Add Debug Prints
1. 添加调试打印
move
use std::debug;
public fun my_function(x: u64, y: u64) {
debug::print(&x);
debug::print(&y);
let result = x + y;
debug::print(&result);
}move
use std::debug;
public fun my_function(x: u64, y: u64) {
debug::print(&x);
debug::print(&y);
let result = x + y;
debug::print(&result);
}2. Simplify Code
2. 简化代码
Break complex expressions into simple steps with debug prints between each step.
将复杂表达式拆解为简单步骤,在每步之间添加调试打印。
3. Test Incrementally
3. 增量测试
Write separate tests for each step of complex logic.
为复杂逻辑的每个步骤编写独立测试。
4. Check Error Codes
4. 检查错误码
move
// When you see: ABORTED: 0x1 (error code 1)
// Find the constant with value 1:
const E_NOT_OWNER: u64 = 1; // This is what failedmove
// 当你看到:ABORTED: 0x1 (错误码1)
// 查找值为1的常量:
const E_NOT_OWNER: u64 = 1; // 这就是失败的原因Custom Error Code Structure
自定义错误码结构
move
// Access control: 1-9
const E_NOT_OWNER: u64 = 1;
const E_NOT_ADMIN: u64 = 2;
const E_UNAUTHORIZED: u64 = 3;
// Input validation: 10-19
const E_ZERO_AMOUNT: u64 = 10;
const E_AMOUNT_TOO_HIGH: u64 = 11;
const E_INVALID_ADDRESS: u64 = 12;
// State errors: 20-29
const E_NOT_INITIALIZED: u64 = 20;
const E_ALREADY_INITIALIZED: u64 = 21;
const E_PAUSED: u64 = 22;
// Business logic: 30+
const E_INSUFFICIENT_BALANCE: u64 = 30;move
// 权限控制:1-9
const E_NOT_OWNER: u64 = 1;
const E_NOT_ADMIN: u64 = 2;
const E_UNAUTHORIZED: u64 = 3;
// 输入校验:10-19
const E_ZERO_AMOUNT: u64 = 10;
const E_AMOUNT_TOO_HIGH: u64 = 11;
const E_INVALID_ADDRESS: u64 = 12;
// 状态错误:20-29
const E_NOT_INITIALIZED: u64 = 20;
const E_ALREADY_INITIALIZED: u64 = 21;
const E_PAUSED: u64 = 22;
// 业务逻辑:30及以上
const E_INSUFFICIENT_BALANCE: u64 = 30;ALWAYS Rules
必须遵守的规则
- Read error messages completely - Don't guess from partial info
- Check error codes - Identify which assertion failed
- Use debug::print - Add debugging output systematically
- Test incrementally - Don't test everything at once
- Define clear error constants - No magic numbers
- Verify fixes with tests - Ensure error doesn't recur
- 完整阅读错误信息 - 不要基于部分信息猜测问题
- 检查错误码 - 定位失败的断言
- 使用debug::print - 系统性地添加调试输出
- 增量测试 - 不要一次性测试所有功能
- 定义清晰的错误常量 - 不要使用魔法数字
- 通过测试验证修复 - 确保错误不再复发
NEVER Rules
禁止操作
- Never ignore compiler warnings - They often indicate real bugs
- Never use generic error codes - Always define descriptive constants
- Never skip testing after fixing - Regression tests are critical
- Never deploy with known errors - Fix all errors before deployment
- Never assume error location - Verify with debug prints
- Never read or
~/.aptos/config.yamlto debug issues — these contain private keys; use.envto verify account config insteadaptos account list - Never run or
echo $VITE_MODULE_PUBLISHER_ACCOUNT_PRIVATE_KEY— these expose private keysenv | grep KEY - Never display private key values that appear in error output or user messages
- 不要忽略编译器警告 - 它们通常指向真实的Bug
- 不要使用通用错误码 - 始终定义描述性的常量
- 修复后不要跳过测试 - 回归测试至关重要
- 不要部署存在已知错误的代码 - 部署前修复所有错误
- 不要假设错误位置 - 通过调试打印验证
- 不要读取或
~/.aptos/config.yaml来调试问题 —— 这些文件包含私钥;请改用.env验证账户配置aptos account list - **不要运行或
echo $VITE_MODULE_PUBLISHER_ACCOUNT_PRIVATE_KEY命令 —— 这些操作会暴露私钥env | grep KEY - 不要展示出现在错误输出或用户消息中的私钥值
References
参考资料
Official Documentation:
- Move Book: https://aptos.dev/build/smart-contracts/book
- Error Codes: https://aptos.dev/build/smart-contracts/book/abort-and-assert
Related Skills:
- - Write correct code to avoid errors
write-contracts - - Test for errors proactively
generate-tests - - Find potential issues before deployment
security-audit
Remember: Object errors are the most common in V2. Check seeds, creator addresses, and which signer you're using.
官方文档:
- Move Book: https://aptos.dev/build/smart-contracts/book
- 错误码说明: https://aptos.dev/build/smart-contracts/book/abort-and-assert
相关技能:
- - 编写正确代码避免错误
write-contracts - - 主动测试提前发现错误
generate-tests - - 部署前发现潜在问题
security-audit
提示: 对象错误是V2版本中最常见的问题。请检查seed、创建者地址以及你使用的签名者是否正确。