Skip to content

Commit 1946be7

Browse files
authored
Merge pull request #11 from AztecProtocol/feat/error-lookup-tool
feat: add aztec_lookup_error tool for error diagnosis
2 parents 32740c3 + 14a278b commit 1946be7

File tree

10 files changed

+1098
-0
lines changed

10 files changed

+1098
-0
lines changed

src/data/error-catalog.ts

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* Static error catalog for Aztec errors that need curated cause/fix text.
3+
* Only entries whose meaning can't be auto-extracted from source files belong here.
4+
*/
5+
6+
export interface ErrorEntry {
7+
id: string;
8+
name: string;
9+
category: ErrorCategory;
10+
/** Strings to match against (lowercased at match time) */
11+
patterns: string[];
12+
cause: string;
13+
fix: string;
14+
source: string;
15+
hexSignature?: string;
16+
errorCode?: number;
17+
}
18+
19+
export type ErrorCategory =
20+
| "contract"
21+
| "circuit"
22+
| "tx-validation"
23+
| "l1"
24+
| "avm"
25+
| "sequencer"
26+
| "operator"
27+
| "general";
28+
29+
/**
30+
* Curated entries for errors whose cause/fix can't be auto-extracted from source.
31+
* Circuit error codes, AVM error types, and common contract assertion messages.
32+
*/
33+
export const STATIC_ERROR_CATALOG: ErrorEntry[] = [
34+
// --- Circuit error codes (from debugging.md prose / protocol docs) ---
35+
{
36+
id: "circuit-2002",
37+
name: "Invalid contract address",
38+
category: "circuit",
39+
patterns: ["2002"],
40+
cause: "The contract address computed by the circuit doesn't match the expected address. Often caused by a mismatch between the deployer, salt, or initialization hash.",
41+
fix: "Verify the contract deployment parameters (deployer address, salt, constructor args). Redeploy if needed.",
42+
source: "protocol circuit",
43+
errorCode: 2002,
44+
},
45+
{
46+
id: "circuit-2005",
47+
name: "Note hash mismatch (private)",
48+
category: "circuit",
49+
patterns: ["2005"],
50+
cause: "A note hash read in the private kernel doesn't match any committed note hash in the tree.",
51+
fix: "Ensure the note you're reading hasn't been nullified and was included in a prior block. Check that note contents match exactly.",
52+
source: "protocol circuit",
53+
errorCode: 2005,
54+
},
55+
{
56+
id: "circuit-2006",
57+
name: "Nullifier already exists",
58+
category: "circuit",
59+
patterns: ["2006"],
60+
cause: "Attempted to create a nullifier that already exists in the nullifier tree.",
61+
fix: "The note may have already been consumed. Check for double-spend logic in your contract.",
62+
source: "protocol circuit",
63+
errorCode: 2006,
64+
},
65+
{
66+
id: "circuit-2017",
67+
name: "Public data tree inconsistency",
68+
category: "circuit",
69+
patterns: ["2017"],
70+
cause: "The public data read or write doesn't match the expected tree state.",
71+
fix: "Ensure public state reads are consistent. If the state was modified concurrently, retry the transaction.",
72+
source: "protocol circuit",
73+
errorCode: 2017,
74+
},
75+
{
76+
id: "circuit-3001",
77+
name: "App circuit proof verification failed",
78+
category: "circuit",
79+
patterns: ["3001"],
80+
cause: "The proof generated by the application circuit failed verification in the kernel circuit.",
81+
fix: "Check for constraint failures in your contract. Run with debug logging to see which assertion failed.",
82+
source: "protocol circuit",
83+
errorCode: 3001,
84+
},
85+
{
86+
id: "circuit-3005",
87+
name: "Kernel circuit validation error",
88+
category: "circuit",
89+
patterns: ["3005"],
90+
cause: "The kernel circuit detected an invalid state transition or constraint violation.",
91+
fix: "Review your transaction's private function calls and ensure all inputs satisfy circuit constraints.",
92+
source: "protocol circuit",
93+
errorCode: 3005,
94+
},
95+
{
96+
id: "circuit-4007",
97+
name: "Rollup base proof failure",
98+
category: "circuit",
99+
patterns: ["4007"],
100+
cause: "The base rollup circuit couldn't verify the kernel proof or tree insertions.",
101+
fix: "This usually indicates a sequencer-side issue. Check the sequencer logs for more context.",
102+
source: "protocol circuit",
103+
errorCode: 4007,
104+
},
105+
{
106+
id: "circuit-4008",
107+
name: "Rollup merge proof failure",
108+
category: "circuit",
109+
patterns: ["4008"],
110+
cause: "The merge rollup circuit couldn't verify the child rollup proofs.",
111+
fix: "This is typically a sequencer/prover issue. Check prover logs and ensure the proving system is up to date.",
112+
source: "protocol circuit",
113+
errorCode: 4008,
114+
},
115+
{
116+
id: "circuit-7008",
117+
name: "Public VM execution failure",
118+
category: "circuit",
119+
patterns: ["7008"],
120+
cause: "The AVM circuit detected an execution error during public function simulation.",
121+
fix: "Check your public function for runtime errors (out-of-bounds access, assertion failures, gas exhaustion).",
122+
source: "protocol circuit",
123+
errorCode: 7008,
124+
},
125+
{
126+
id: "circuit-7009",
127+
name: "AVM proof verification failure",
128+
category: "circuit",
129+
patterns: ["7009"],
130+
cause: "The AVM proof failed verification, typically due to a mismatch between execution trace and proof.",
131+
fix: "Re-simulate the public function. If persistent, report as a potential prover bug.",
132+
source: "protocol circuit",
133+
errorCode: 7009,
134+
},
135+
136+
// --- AVM error types ---
137+
{
138+
id: "avm-out-of-gas",
139+
name: "OutOfGasError",
140+
category: "avm",
141+
patterns: ["outofgaserror", "out of gas", "outofgas"],
142+
cause: "The public function execution ran out of gas (L2 or DA gas).",
143+
fix: "Increase the gas limit in your transaction request, or optimize the function to use less gas. Check both L2 gas and DA gas limits.",
144+
source: "AVM execution",
145+
},
146+
{
147+
id: "avm-tag-check",
148+
name: "TagCheckError",
149+
category: "avm",
150+
patterns: ["tagcheckerror", "tag check", "tagcheck"],
151+
cause: "An AVM instruction received an operand with an unexpected type tag (e.g., used a field where a u32 was expected).",
152+
fix: "Check your public function for type mismatches. Ensure casts are correct and storage reads return the expected types.",
153+
source: "AVM execution",
154+
},
155+
{
156+
id: "avm-invalid-opcode",
157+
name: "InvalidOpcodeError",
158+
category: "avm",
159+
patterns: ["invalidopcodeerror", "invalid opcode"],
160+
cause: "The AVM encountered an unrecognized instruction opcode.",
161+
fix: "Recompile your contract with a compatible version of the Aztec compiler. The bytecode may be from an incompatible version.",
162+
source: "AVM execution",
163+
},
164+
{
165+
id: "avm-revert",
166+
name: "Assertion failure / Revert",
167+
category: "avm",
168+
patterns: ["avmreverterror", "avm revert", "explicit revert"],
169+
cause: "A public function hit an assert!() or explicitly reverted.",
170+
fix: "Check the revert data for the assertion message. Review the public function logic to understand which condition failed.",
171+
source: "AVM execution",
172+
},
173+
174+
// --- Common contract assertion messages ---
175+
{
176+
id: "contract-not-owner",
177+
name: "Caller is not the owner",
178+
category: "contract",
179+
patterns: ["caller is not the owner", "not the owner", "unauthorized owner"],
180+
cause: "The function requires the caller to be the contract owner, but a different address called it.",
181+
fix: "Call the function from the owner account, or update the contract's owner if appropriate.",
182+
source: "contract assertion",
183+
},
184+
{
185+
id: "contract-insufficient-balance",
186+
name: "Insufficient balance",
187+
category: "contract",
188+
patterns: ["insufficient balance", "balance too low", "not enough balance"],
189+
cause: "The account doesn't have enough tokens to complete the transfer or operation.",
190+
fix: "Ensure the sender has sufficient token balance. Check both private and public balances as appropriate.",
191+
source: "contract assertion",
192+
},
193+
{
194+
id: "contract-already-initialized",
195+
name: "Contract already initialized",
196+
category: "contract",
197+
patterns: ["already initialized", "already been initialized"],
198+
cause: "Attempted to call an initializer on a contract that has already been initialized.",
199+
fix: "Each contract can only be initialized once. Deploy a new instance if you need a fresh contract.",
200+
source: "contract assertion",
201+
},
202+
{
203+
id: "contract-not-initialized",
204+
name: "Contract not initialized",
205+
category: "contract",
206+
patterns: ["not initialized", "must be initialized"],
207+
cause: "Attempted to call a function on a contract that hasn't been initialized yet.",
208+
fix: "Call the contract's initializer function before using other functions.",
209+
source: "contract assertion",
210+
},
211+
{
212+
id: "contract-invalid-nonce",
213+
name: "Invalid nonce",
214+
category: "contract",
215+
patterns: ["invalid nonce", "nonce mismatch"],
216+
cause: "The transaction nonce doesn't match the expected value, often due to a concurrent transaction.",
217+
fix: "Retry the transaction. If using authwits, ensure the nonce in the authwit matches the transaction.",
218+
source: "contract assertion",
219+
},
220+
];

src/index.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
listAztecExamples,
2424
readAztecExample,
2525
readRepoFile,
26+
lookupAztecError,
2627
} from "./tools/index.js";
2728
import {
2829
formatSyncResult,
@@ -31,6 +32,7 @@ import {
3132
formatExamplesList,
3233
formatExampleContent,
3334
formatFileContent,
35+
formatErrorLookupResult,
3436
} from "./utils/format.js";
3537
import { MCP_VERSION } from "./version.js";
3638
import { getSyncState, writeAutoResyncAttempt } from "./utils/sync-metadata.js";
@@ -190,6 +192,33 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
190192
required: ["path"],
191193
},
192194
},
195+
{
196+
name: "aztec_lookup_error",
197+
description:
198+
"Look up an Aztec error by message, error code, or hex signature. " +
199+
"Returns root cause and suggested fix. Searches Solidity errors, " +
200+
"TX validation errors, circuit codes, AVM errors, and documentation.",
201+
inputSchema: {
202+
type: "object",
203+
properties: {
204+
query: {
205+
type: "string",
206+
description:
207+
"Error message, numeric error code (e.g., '2002'), or hex signature (e.g., '0xa5b2ba17')",
208+
},
209+
category: {
210+
type: "string",
211+
description:
212+
"Filter by error category. Options: contract, circuit, tx-validation, l1, avm, sequencer, operator, general",
213+
},
214+
maxResults: {
215+
type: "number",
216+
description: "Maximum results to return (default: 10)",
217+
},
218+
},
219+
required: ["query"],
220+
},
221+
},
193222
],
194223
}));
195224

@@ -201,6 +230,7 @@ function validateToolRequest(name: string, args: Record<string, unknown> | undef
201230
break;
202231
case "aztec_search_code":
203232
case "aztec_search_docs":
233+
case "aztec_lookup_error":
204234
if (!args?.query) throw new McpError(ErrorCode.InvalidParams, "query is required");
205235
break;
206236
case "aztec_read_example":
@@ -365,6 +395,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
365395
break;
366396
}
367397

398+
case "aztec_lookup_error": {
399+
const result = lookupAztecError({
400+
query: args!.query as string,
401+
category: args?.category as string | undefined,
402+
maxResults: args?.maxResults as number | undefined,
403+
});
404+
text = formatErrorLookupResult(result);
405+
break;
406+
}
407+
368408
}
369409

370410
return {

src/repos/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,16 @@ const BASE_REPOS: Omit<RepoConfig, "tag">[] = [
4545
"barretenberg/ts/src",
4646
"boxes",
4747
"playground",
48+
"l1-contracts/src/core/libraries/Errors.sol",
4849
],
4950
sparsePathOverrides: [
5051
{
5152
paths: [
5253
"docs/developer_versioned_docs/version-{version}",
5354
"docs/static/aztec-nr-api/devnet",
5455
"docs/static/typescript-api/devnet",
56+
"docs/docs-developers/docs/aztec-nr/debugging.md",
57+
"docs/docs-operate/operators/operator-faq.md",
5558
],
5659
branch: "next",
5760
},

src/tools/error-lookup.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Error lookup tool — diagnose any Aztec error by message, code, or hex signature.
3+
*/
4+
5+
import { lookupError } from "../utils/error-lookup.js";
6+
import type { ErrorLookupResult } from "../utils/error-lookup.js";
7+
8+
export function lookupAztecError(options: {
9+
query: string;
10+
category?: string;
11+
maxResults?: number;
12+
}): {
13+
success: boolean;
14+
result: ErrorLookupResult;
15+
message: string;
16+
} {
17+
const { query, category, maxResults = 10 } = options;
18+
19+
const result = lookupError(query, { category, maxResults });
20+
21+
const totalMatches = result.catalogMatches.length + result.codeMatches.length;
22+
23+
return {
24+
success: true,
25+
result,
26+
message:
27+
totalMatches > 0
28+
? `Found ${result.catalogMatches.length} known error(s) and ${result.codeMatches.length} code reference(s) for "${query}"`
29+
: `No matches found for "${query}". Try a different error message, code, or hex signature.`,
30+
};
31+
}

src/tools/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export {
1010
readAztecExample,
1111
readRepoFile,
1212
} from "./search.js";
13+
export { lookupAztecError } from "./error-lookup.js";

src/tools/sync.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { join } from "path";
77
import { AZTEC_REPOS, getAztecRepos, DEFAULT_AZTEC_VERSION, RepoConfig } from "../repos/config.js";
88
import { cloneRepo, getReposStatus, getNoirCommitFromAztec, getRepoPath, REPOS_DIR, Logger } from "../utils/git.js";
99
import { writeSyncMetadata, stampMetadataMcpVersion, readSyncMetadata, SyncMetadata } from "../utils/sync-metadata.js";
10+
import { clearErrorCache } from "../utils/error-lookup.js";
1011

1112
export interface SyncResult {
1213
success: boolean;
@@ -203,6 +204,9 @@ export async function syncRepos(options: {
203204
}
204205
}
205206

207+
// Invalidate cached error entries so the next lookup re-parses from updated files
208+
clearErrorCache();
209+
206210
const message = !allSuccess
207211
? "Some repositories failed to sync"
208212
: metadataWriteFailed

0 commit comments

Comments
 (0)