Skip to content

fix(server-hono): don't double-prefix basePath when Hono already merged it into route.path#1241

Merged
omeraplak merged 1 commit intoVoltAgent:mainfrom
truffle-dev:fix/server-hono-custom-routes-double-prefix
Apr 25, 2026
Merged

fix(server-hono): don't double-prefix basePath when Hono already merged it into route.path#1241
omeraplak merged 1 commit intoVoltAgent:mainfrom
truffle-dev:fix/server-hono-custom-routes-double-prefix

Conversation

@truffle-dev
Copy link
Copy Markdown
Contributor

@truffle-dev truffle-dev commented Apr 24, 2026

Fixes #1238.

Root cause

When a sub-app is mounted via app.route(basePath, subApp) or app.basePath(basePath), Hono's internal _addRoute calls mergePath(this._basePath, path) and stores the merged result in route.path, while still keeping basePath on the route as metadata:

// from hono/src/hono-base.ts _addRoute
path = mergePath(this._basePath, path)
const r: RouterRoute = { basePath: this._basePath, path, method, handler }

So for the reporter's setup:

const routes = new Hono();
routes.get("/hello", ...);
app.route("/api", routes);

the entry that lands in app.routes is { method: "GET", path: "/api/hello", basePath: "/api" }. extractCustomEndpoints then did:

const rawPath = route.basePath ? `${route.basePath}${route.path}` : route.path;
// => "/api" + "/api/hello" = "/api/api/hello"

Normalized that to /api/api/hello, which is what shows up in the startup log. The actual request still worked because Hono itself routes by route.path; only the logging / OpenAPI side was wrong.

Fix

Only prepend basePath when route.path does not already include it:

const basePath = route.basePath && route.basePath !== "/" ? route.basePath : "";
const rawPath =
  basePath && !route.path.startsWith(basePath)
    ? `${basePath}${route.path}`
    : route.path;

This keeps the existing behavior for the defensive mock case { path: "/health", basePath: "/api" } (still becomes /api/health), while producing /api/hello for the shape Hono actually hands us.

Tests

Two regression tests added to custom-endpoints.spec.ts:

  1. should not double-prefix basePath when Hono already merged it into path - models app.route('/api', routes); routes.get('/hello').
  2. should handle nested sub-apps without double-prefixing - models app.route('/api', sub); sub.route('/sub', inner); inner.get('/x').

Both fail without the fix (producing /api/api/hello and /api/api/sub/inner respectively). All 44 existing tests still pass. Full spec: 46/46 green.

 ✓ src/utils/custom-endpoints.spec.ts (46 tests) 25ms
 Test Files  1 passed (1)
      Tests  46 passed (46)

Summary by cubic

Fixes custom endpoint extraction in @voltagent/server-hono to avoid double-prefixing basePath when Hono already merged it into route.path. Logs and OpenAPI now show correct paths for sub-apps; request routing was unaffected.

  • Bug Fixes
    • Only prepend basePath when route.path doesn’t start with it.
    • Added regression tests for sub-app and nested sub-app cases.

Written for commit 85cae43. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed route path logging that was incorrectly duplicating the base path prefix (e.g., /api/api/hello now logs as /api/hello)
  • Tests

    • Added test coverage for path normalization in nested routing scenarios

…ed it into route.path

Hono merges basePath into route.path when a sub-app is mounted via
app.route(basePath, subApp) or app.basePath(basePath), but keeps basePath
on the route as metadata. extractCustomEndpoints blindly prepended basePath
to route.path, so a route served at /api/hello was logged as /api/api/hello.

Only prepend basePath when route.path does not already include it. Add two
regression tests for the real-Hono shape (reporter case and a nested
sub-app).

Fixes VoltAgent#1238
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 24, 2026

🦋 Changeset detected

Latest commit: 85cae43

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

This PR includes changesets to release 1 package
Name Type
@voltagent/server-hono 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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

A patch to @voltagent/server-hono is introduced to prevent basePath from being prepended twice during custom endpoint extraction. The fix detects when Hono has already merged basePath into route.path and conditionally prepends only when necessary. Tests validate the corrected behavior.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/custom-routes-no-double-prefix.md
Documents patch fix for duplicate basePath prepending in custom endpoint extraction. Clarifies when basePath is already merged into route.path.
Custom Endpoint Utilities
packages/server-hono/src/utils/custom-endpoints.ts
Adds conditional logic to check if route.path already starts with basePath before prepending, preventing duplicated paths.
Unit Tests
packages/server-hono/src/utils/custom-endpoints.spec.ts
Adds two test cases validating path normalization for single routed apps and nested sub-app scenarios where basePath is already merged.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A path once doubled, now made right,
No more /api/api in sight!
With Hono's merge, we check with care,
One basePath only—clean and fair! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: fixing double-prefixing of basePath in custom endpoint extraction when Hono already merged it into route.path.
Description check ✅ Passed The PR description is comprehensive and complete, addressing all required template sections: root cause explanation with code examples, detailed fix implementation, test coverage with specific regression test cases, and verification that all 46 tests pass.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

truffle-dev added a commit to truffle-dev/story that referenced this pull request Apr 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/server-hono/src/utils/custom-endpoints.ts (1)

47-50: Optional: consider a segment-boundary check for startsWith.

route.path.startsWith(basePath) is a raw string-prefix match, so a pathological input like basePath="/api" with route.path="/apiary" would skip the prepend and yield /apiary instead of /api/apiary. In practice this shouldn't occur — when Hono populates route.basePath, route.path is guaranteed to be the merged absolute path with a segment boundary — so this is a defensive nit, not a bug.

🔧 Optional hardening
-          const basePath = route.basePath && route.basePath !== "/" ? route.basePath : "";
-          const rawPath =
-            basePath && !route.path.startsWith(basePath)
-              ? `${basePath}${route.path}`
-              : route.path;
+          const basePath = route.basePath && route.basePath !== "/" ? route.basePath : "";
+          const alreadyPrefixed =
+            !!basePath &&
+            (route.path === basePath || route.path.startsWith(`${basePath}/`));
+          const rawPath = basePath && !alreadyPrefixed ? `${basePath}${route.path}` : route.path;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/server-hono/src/utils/custom-endpoints.ts` around lines 47 - 50, The
startsWith check for basePath is too permissive and can mis-detect segment
boundaries (e.g., basePath="/api" vs route.path="/apiary"); update the rawPath
logic in custom-endpoints.ts to only treat route.path as already prefixed when
it equals basePath or when the next character after basePath is a path
separator. Concretely, replace the plain route.path.startsWith(basePath) test
with a segment-aware check such as: consider it prefixed only if route.path ===
basePath or route.path.startsWith(basePath + '/'), and otherwise prepend
basePath when computing rawPath.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/server-hono/src/utils/custom-endpoints.ts`:
- Around line 47-50: The startsWith check for basePath is too permissive and can
mis-detect segment boundaries (e.g., basePath="/api" vs route.path="/apiary");
update the rawPath logic in custom-endpoints.ts to only treat route.path as
already prefixed when it equals basePath or when the next character after
basePath is a path separator. Concretely, replace the plain
route.path.startsWith(basePath) test with a segment-aware check such as:
consider it prefixed only if route.path === basePath or
route.path.startsWith(basePath + '/'), and otherwise prepend basePath when
computing rawPath.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 705758cf-6d68-4a2e-8a4f-f661603fb697

📥 Commits

Reviewing files that changed from the base of the PR and between 0b793d9 and 85cae43.

📒 Files selected for processing (3)
  • .changeset/custom-routes-no-double-prefix.md
  • packages/server-hono/src/utils/custom-endpoints.spec.ts
  • packages/server-hono/src/utils/custom-endpoints.ts

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 3 files

@omeraplak omeraplak merged commit 794da98 into VoltAgent:main Apr 25, 2026
20 of 23 checks passed
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.

[BUG] custom routes logging mistake

2 participants