Skip to content

fix: ENSApi initialization logic#2335

Draft
tk-o wants to merge 4 commits into
mainfrom
fix/2272-ensapi-di-init
Draft

fix: ENSApi initialization logic#2335
tk-o wants to merge 4 commits into
mainfrom
fix/2272-ensapi-di-init

Conversation

@tk-o

@tk-o tk-o commented Jun 23, 2026

Copy link
Copy Markdown
Member

Lite PR

Tip: Review docs on the ENSNode PR process

Summary

  • ENSApi now waits for ENSDb to become ready before initializing its dependency graph, preventing startup crashes when ENSApi begins before the relevant ENSNode Metadata record is available in the ENSDb instance.
  • Added waitForEnsDbToBeReady helper in apps/ensapi/src/lib/ensdb.ts with a 15-minute retry window (15 retries × 60 seconds).
  • Added unit tests for the retry helper and updated the DI bootstrap test to cover the retry path.
  • Minor logging improvements in apps/ensapi/src/index.ts to ensure error objects are logged under the err key.

Why


Testing

  • pnpm -F ensapi test297 passed.
  • pnpm -F ensapi typecheckpassed.
  • pnpm typecheck (all workspaces) — passed.
  • pnpm lint on changed files — no issues.

Notes for Reviewer (Optional)

  • The retry loop runs after ensDbClient.isHealthy() has already verified connectivity, so the loop only waits for metadata readiness.
  • The timeout is intentionally hardcoded to 15 minutes; the interval was not made configurable per the implementation plan.
  • The waitForEnsDbToBeReady helper does not cache its promise because di.init() runs once per process.

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

tk-o added 4 commits June 23, 2026 18:53
Sometimes, the ENSNode Metadata record is written to the ENSDb instance with a delay (up to several miunte). This fix allows the DI container initialization logic to wait for up to 15 minutes for the ENSDb instance to become ready and only then proceed with initialization logic that depends on the ENSNode Metadata record presence.
Pass error objects with the `err` field into `logger.error` method call.
Copilot AI review requested due to automatic review settings June 23, 2026 16:56
@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 878fad7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
Name Type
ensapi Patch
ensindexer Patch
ensadmin Patch
ensrainbow Patch
fallback-ensapi Patch
enssdk Patch
enscli Patch
enskit Patch
ensskills Patch
@ensnode/datasources Patch
@ensnode/ensrainbow-sdk Patch
@ensnode/ensdb-sdk Patch
@ensnode/ensnode-sdk Patch
@ensnode/integration-test-env Patch
@ensnode/ponder-sdk Patch
@ensnode/ponder-subgraph Patch
@ensnode/shared-configs Patch
@docs/ensnode Patch
@docs/ensrainbow Patch
@namehash/ens-referrals Patch
@namehash/namehash-ui Patch
@ensnode/ensindexer-perf-testing Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Jun 23, 2026 4:56pm
enskit-react-example.ensnode.io Ready Ready Preview, Comment Jun 23, 2026 4:56pm
ensnode.io Ready Ready Preview, Comment Jun 23, 2026 4:56pm
ensrainbow.io Ready Ready Preview, Comment Jun 23, 2026 4:56pm

@tk-o

tk-o commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

@greptile review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new waitForEnsDbToBeReady function in apps/ensapi/src/lib/ensdb.ts that polls EnsDbReader.isReady() via p-retry (up to 15 attempts, ~60s apart) before the DI container proceeds. The function is integrated into EnsApiDiContainer.init(). Error logging in index.ts is normalized to structured { err } objects, and tests are updated accordingly.

Changes

ENSDb Readiness Gate for DI Init

Layer / File(s) Summary
waitForEnsDbToBeReady implementation and tests
apps/ensapi/src/lib/ensdb.ts, apps/ensapi/src/lib/ensdb.test.ts
New module exports waitForEnsDbToBeReady, using p-retry with 15 attempts and ~60s delay to poll ensDbClient.isReady(). Warns on each failed attempt and rethrows on exhaustion. Tests cover immediate resolution, interval-based retry, and rejection with call-count assertions using fake timers.
DI container wiring and test mock updates
apps/ensapi/src/di.ts, apps/ensapi/src/config/config.singleton.test.ts
Imports waitForEnsDbToBeReady and inserts the awaited call in EnsApiDiContainer.init() before proceeding; updates catch error message format. Test mocks gain warn and isReady() stubs; a new fake-timer test verifies di.init() polls isReady exactly twice before completing.
Structured error logging and changeset
apps/ensapi/src/index.ts, .changeset/ensapi-ensdb-ready.md
Normalizes all logger.error calls in index.ts to use { err } structured objects across DI init failure, graceful shutdown, and uncaught exception handlers. Adds the patch changeset entry.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • namehash/ensnode#2021: Both PRs operate on EnsDbReader.isReady() — this PR adds a polling loop in waitForEnsDbToBeReady that calls it, while that PR modifies the underlying isReady() implementation and its use in the /ready endpoint.

Suggested labels

javascript

🐇 No more crash at the gate,
ENSApi learns to wait!
Fifteen tries, one minute each,
Till ENSDb comes in reach.
Metadata blooms, the graph stands tall — 🌱
No startup race to make us fall!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'fix: ENSApi initialization logic' is vague and does not clearly convey the specific change of adding retry logic for ENSDb readiness checks. Consider a more specific title like 'fix: wait for ENSDb readiness during DI initialization' to clearly describe the primary change.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The PR description comprehensively follows the template with all required sections filled out: Summary, Why, Testing, Notes for Reviewer, and Pre-Review Checklist are all complete and well-detailed.
Linked Issues check ✅ Passed All code changes directly address issue #2272 by implementing the proposed fix: a waitForEnsDbToBeReady helper with bounded retry logic, comprehensive unit tests, and improved error logging.
Out of Scope Changes check ✅ Passed All changes are scoped to the linked issue #2272; the PR adds the retry helper, updates tests, improves logging, and includes a changeset—all directly addressing the startup race condition.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/2272-ensapi-di-init

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes ENSApi startup resilient to ENSDb/indexer cold-start ordering by waiting for ENSDb to report “ready” (i.e., metadata initialized) before continuing DI initialization, avoiding a hard crash when metadata isn’t available yet.

Changes:

  • Added a waitForEnsDbToBeReady retry helper (15 retries × 60s) and wired it into di.init() after the health check and before cache initialization.
  • Added unit tests for the retry helper and extended the DI bootstrap test to cover the retry path.
  • Adjusted logging in index.ts to ensure thrown errors are logged under the err key, and added a Changeset entry for ensapi.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/ensapi/src/lib/ensdb.ts Introduces bounded retry helper that waits for ENSDb readiness before proceeding.
apps/ensapi/src/lib/ensdb.test.ts Adds unit tests validating immediate readiness, retry success, and timeout failure behavior.
apps/ensapi/src/index.ts Normalizes error logging to structured { err } payloads for DI init and shutdown paths.
apps/ensapi/src/di.ts Calls the readiness-wait helper during DI init to prevent early cache reads from crashing startup.
apps/ensapi/src/config/config.singleton.test.ts Adds a bootstrap test that exercises the readiness retry path via fake timers.
.changeset/ensapi-ensdb-ready.md Documents the behavior change and bumps ensapi as a patch.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
},
{
retries: 15, // This allows for a total of over 15 minutes of retries with 1 minute between attempts.
"ensapi": patch
---

ENSApi now waits for ENSDb to become ready before initializing its dependency graph. This prevents startup crashes when ENSApi starts before the relevant ENSNode Metadata record is available in ENSDb instance.
@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a startup crash in ENSApi by introducing a waitForEnsDbToBeReady helper that polls EnsDbReader.isReady() via pRetry (up to 16 total attempts at 60-second fixed intervals, ~15-minute window) before proceeding to initialize caches and the RPC client. It also corrects pino-style structured logging in index.ts (error objects now under the err key).

  • apps/ensapi/src/lib/ensdb.ts — new waitForEnsDbToBeReady helper using pRetry with a hardcoded 15 retry budget at 60 s intervals, called inside di.init() after the isHealthy() check.
  • apps/ensapi/src/lib/ensdb.test.ts — unit tests covering the immediate-ready, retry-until-ready, and exhausted-retries paths with fake timers.
  • apps/ensapi/src/index.ts — logging improvements: error objects now passed as { err } rather than as the first argument to logger.error.

Confidence Score: 4/5

The change is safe to merge. The retry loop is well-tested with fake timers and the integration test correctly exercises the retry path inside di.init().

The core logic — polling isReady() with a fixed 60-second interval for up to 15 retries — is correct and well-covered by tests. The only rough edges are stylistic: a duplicate ERROR log entry when retries are exhausted (ensdb.ts logs then index.ts logs for the same failure), a slightly inaccurate comment ('over 15 minutes' vs exactly 15), and error wrapping that drops the original stack trace. None of these affect runtime correctness.

apps/ensapi/src/lib/ensdb.ts — the catch/re-throw logging pattern warrants a look to avoid duplicate error entries in production logs.

Important Files Changed

Filename Overview
apps/ensapi/src/lib/ensdb.ts New retry helper using pRetry with a fixed 60s interval, up to 15 retries; minor double-logging issue when all retries are exhausted.
apps/ensapi/src/di.ts Inserts waitForEnsDbToBeReady between health check and cache init; error wrapping still loses the original stack trace (pre-existing pattern).
apps/ensapi/src/lib/ensdb.test.ts Good coverage of the three key paths (immediate, retry, exhausted) using fake timers correctly.
apps/ensapi/src/config/config.singleton.test.ts Integration-level test for the retry path in di.init(); fake-timer setup/teardown is correct but spy targets the mocked class prototype, which works because the mock replaces EnsDbReader wholesale.
apps/ensapi/src/index.ts Logging fixes only — error objects now correctly under the err key for pino structured logging.
.changeset/ensapi-ensdb-ready.md Changeset entry for a patch release of ensapi.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant idx as index.ts
    participant di as EnsApiDiContainer
    participant ensdb as waitForEnsDbToBeReady
    participant pRetry as pRetry
    participant client as EnsDbReader

    idx->>di: di.init() [void, non-blocking]
    di->>di: loadContext() [sync]
    di->>client: isHealthy()
    client-->>di: true
    di->>ensdb: waitForEnsDbToBeReady(ensDbClient)
    ensdb->>pRetry: "pRetry(fn, {retries:15, timeout:60s})"
    loop Up to 16 attempts (1 initial + 15 retries)
        pRetry->>client: isReady()
        alt ready
            client-->>pRetry: true
            pRetry-->>ensdb: resolves
        else not ready
            client-->>pRetry: false
            pRetry->>pRetry: wait 60s, onFailedAttempt warn log
        end
    end
    alt all retries exhausted
        pRetry-->>ensdb: throws FailedAttemptError
        ensdb->>ensdb: error log
        ensdb-->>di: throws
        di-->>di: wraps error, throws
        di-->>idx: rejects
        idx->>idx: "logger.error({err}), process.exit(1)"
    else ready
        ensdb-->>di: resolves
        di->>di: init caches (Promise.all)
        di->>client: getBlockNumber() [RPC check]
        di-->>idx: resolves
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant idx as index.ts
    participant di as EnsApiDiContainer
    participant ensdb as waitForEnsDbToBeReady
    participant pRetry as pRetry
    participant client as EnsDbReader

    idx->>di: di.init() [void, non-blocking]
    di->>di: loadContext() [sync]
    di->>client: isHealthy()
    client-->>di: true
    di->>ensdb: waitForEnsDbToBeReady(ensDbClient)
    ensdb->>pRetry: "pRetry(fn, {retries:15, timeout:60s})"
    loop Up to 16 attempts (1 initial + 15 retries)
        pRetry->>client: isReady()
        alt ready
            client-->>pRetry: true
            pRetry-->>ensdb: resolves
        else not ready
            client-->>pRetry: false
            pRetry->>pRetry: wait 60s, onFailedAttempt warn log
        end
    end
    alt all retries exhausted
        pRetry-->>ensdb: throws FailedAttemptError
        ensdb->>ensdb: error log
        ensdb-->>di: throws
        di-->>di: wraps error, throws
        di-->>idx: rejects
        idx->>idx: "logger.error({err}), process.exit(1)"
    else ready
        ensdb-->>di: resolves
        di->>di: init caches (Promise.all)
        di->>client: getBlockNumber() [RPC check]
        di-->>idx: resolves
    end
Loading

Reviews (1): Last reviewed commit: "Improve error logging" | Re-trigger Greptile

Comment on lines +53 to +61
.catch((err) => {
logger.error(
{ ensIndexerSchemaName, err },
`ENSDb readiness check failed after multiple attempts`,
);

// Throw the error to terminate the ENSApi process due to the failed readiness check of a critical dependency
throw err;
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The .catch() block inside waitForEnsDbToBeReady logs the error at ERROR level and then re-throws. The caller in di.ts re-throws a wrapped error, which is then caught and logged again at ERROR level by index.ts. This means a single readiness-exhaustion event produces two ERROR log entries — one from ensdb.ts and one from index.ts — with different shapes and stack traces, which can confuse operators. Removing the logger.error call here (keeping only the re-throw) leaves a single, structured ERROR log at the index.ts boundary where process.exit(1) is also triggered.

Suggested change
.catch((err) => {
logger.error(
{ ensIndexerSchemaName, err },
`ENSDb readiness check failed after multiple attempts`,
);
// Throw the error to terminate the ENSApi process due to the failed readiness check of a critical dependency
throw err;
});
.catch((err) => {
// Throw the error to terminate the ENSApi process due to the failed readiness check of a critical dependency.
// The caller (di.init → index.ts) is responsible for logging and exiting.
throw err;
});

}
},
{
retries: 15, // This allows for a total of over 15 minutes of retries with 1 minute between attempts.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The inline comment says "over 15 minutes" but the math is exactly 15 minutes: 15 retry intervals × 60 s = 900 s. "Over" implies more than 15, which is slightly misleading. Consider "up to 15 minutes" or "exactly 15 minutes".

Suggested change
retries: 15, // This allows for a total of over 15 minutes of retries with 1 minute between attempts.
retries: 15, // This allows for a total of up to 15 minutes of retries (15 retries × 60 s interval).

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread apps/ensapi/src/di.ts
Comment on lines 276 to +278
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
throw new Error(
`DI container initialization failed: could not connect to ENSDb due to ${errorMessage}`,
);
throw new Error(`DI container initialization failed: ${errorMessage}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The catch block extracts only the error message string and discards the original error object, losing its stack trace. Using the cause option preserves the original for structured loggers and tooling that can surface error chains.

Suggested change
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
throw new Error(
`DI container initialization failed: could not connect to ENSDb due to ${errorMessage}`,
);
throw new Error(`DI container initialization failed: ${errorMessage}`);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
throw new Error(`DI container initialization failed: ${errorMessage}`, { cause: error });

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/ensapi/src/lib/ensdb.test.ts`:
- Around line 54-67: The test suite for waitForEnsDbToBeReady currently only
covers the case where isReady returns false, but does not test the scenario
where isReady() throws or rejects. Add a new test case that creates an
EnsDbClient with an isReady mock that throws an error, then verify that
waitForEnsDbToBeReady immediately rejects with that error without attempting any
retries. This ensures the fail-fast behavior is preserved when isReady() throws
rather than returns false.

In `@apps/ensapi/src/lib/ensdb.ts`:
- Around line 25-33: The retry logic in the ensDbClient.isReady() check does not
distinguish between an intentional "not ready" signal (which should trigger
retry) and actual exceptions thrown by isReady() (which may be non-transient
infrastructure issues). Wrap the ensDbClient.isReady() call in a try-catch block
to separate these two failure modes: when isReady() returns false, throw an
error to trigger retry as intended, but when isReady() throws an exception,
handle it separately (either fail fast or apply different retry logic).
Additionally, add test coverage for scenarios where the isReady() method itself
throws an exception, not just when it returns false.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6967f067-2258-4d9e-b9fe-7c4775036151

📥 Commits

Reviewing files that changed from the base of the PR and between 910f589 and 878fad7.

📒 Files selected for processing (6)
  • .changeset/ensapi-ensdb-ready.md
  • apps/ensapi/src/config/config.singleton.test.ts
  • apps/ensapi/src/di.ts
  • apps/ensapi/src/index.ts
  • apps/ensapi/src/lib/ensdb.test.ts
  • apps/ensapi/src/lib/ensdb.ts

Comment on lines +54 to +67
it("rejects when the ENSDb instance never becomes ready", async () => {
const ensDbClient = createEnsDbClient({ isReady: false });

const promise = waitForEnsDbToBeReady(ensDbClient);
// Attach a no-op catch to prevent the promise from being reported as an
// unhandled rejection while the fake timers are advancing.
promise.catch(() => {});

// Advance past all 15 retry intervals (15 × 60s = 900s).
await vi.advanceTimersByTimeAsync(900_000);

await expect(promise).rejects.toThrow("ENSDb instance is not ready yet.");
expect(ensDbClient.isReady).toHaveBeenCalledTimes(16); // initial + 15 retries
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add a regression test for the isReady() rejection path.

Current cases cover boolean readiness states but not isReady() throwing/rejecting. Add one test that asserts immediate rejection (and no long retry loop) so fail-fast behavior is locked in.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/lib/ensdb.test.ts` around lines 54 - 67, The test suite for
waitForEnsDbToBeReady currently only covers the case where isReady returns
false, but does not test the scenario where isReady() throws or rejects. Add a
new test case that creates an EnsDbClient with an isReady mock that throws an
error, then verify that waitForEnsDbToBeReady immediately rejects with that
error without attempting any retries. This ensures the fail-fast behavior is
preserved when isReady() throws rather than returns false.

Comment on lines +25 to +33
return pRetry(
async () => {
const isEnsDbInstanceReady = await ensDbClient.isReady();

if (!isEnsDbInstanceReady) {
throw new Error("ENSDb instance is not ready yet.");
}
},
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only verification for retry scope + missing rejection-path coverage.
sed -n '25,48p apps/ensapi/src/lib/ensdb.ts'
rg -n "mockRejectedValue|rejects.*isReady|rejects.*connection|isReady\\(\\).*throw" apps/ensapi/src/lib/ensdb.test.ts

Repository: namehash/ensnode

Length of output: 216


🏁 Script executed:

#!/bin/bash
# Read lines 25-48 from the target file
cat -n apps/ensapi/src/lib/ensdb.ts | sed -n '25,48p'

echo "---"

# Search for test coverage of error/rejection paths
rg -A5 "mockRejectedValue|rejects|isReady.*throw|Error.*ENSDb" apps/ensapi/src/lib/ensdb.test.ts | head -50

Repository: namehash/ensnode

Length of output: 1643


🏁 Script executed:

#!/bin/bash
# Check for complete test coverage and other error scenarios
echo "=== Looking for all tests in ensdb.test.ts ==="
rg -n "it\(" apps/ensapi/src/lib/ensdb.test.ts

echo ""
echo "=== Check if there are tests for thrown errors from isReady ==="
rg -B3 "throw.*isReady|isReady.*throw" apps/ensapi/src/lib/ensdb.test.ts

echo ""
echo "=== Read the complete waitForEnsDbToBeReady function ==="
sed -n '21,62p' apps/ensapi/src/lib/ensdb.ts

Repository: namehash/ensnode

Length of output: 1977


Retry logic conflates transient "not ready" with non-retryable errors from isReady().

The retry mechanism treats all errors within the operation callback identically. If isReady() throws an exception (network failure, timeout, misconfiguration), it will retry the full 15-minute window instead of failing fast. This masks infrastructure problems during startup.

Consider distinguishing between intentional "not ready" signals (deserving retry) and exceptions from isReady() itself (potentially non-transient). Additionally, no test coverage exists for scenarios where isReady() throws—only for when it returns false.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/lib/ensdb.ts` around lines 25 - 33, The retry logic in the
ensDbClient.isReady() check does not distinguish between an intentional "not
ready" signal (which should trigger retry) and actual exceptions thrown by
isReady() (which may be non-transient infrastructure issues). Wrap the
ensDbClient.isReady() call in a try-catch block to separate these two failure
modes: when isReady() returns false, throw an error to trigger retry as
intended, but when isReady() throws an exception, handle it separately (either
fail fast or apply different retry logic). Additionally, add test coverage for
scenarios where the isReady() method itself throws an exception, not just when
it returns false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ENSApi: DI init crashes instead of waiting when ENSIndexer metadata isn't published yet

2 participants