Skip to content

Conversation

@L33Z22L11
Copy link
Contributor

🔗 Linked issue

introduced by #30973

📚 Description

When using useAsyncData('posts/%', ...) in multiple pages, some pages occasionally receive an empty payload.

After investigation, the issue was traced to normalizeFsKey, where decodeURIComponent throws a "URI malformed" error if the key contains a literal % (not part of valid URI encoding). This breaks cache key normalization and causes cache misses.


🧪 Reproduction

  1. Use the same key on multiple pages:

    await useAsyncData('posts/%', fetchPosts)
  2. Pre-render or visit both pages

  3. One of them may get an empty payload


🛠 Fix

Add a try/catch fallback around decodeURIComponent inside normalizeFsKey. If decoding fails, fall back to the normalized key. This ensures keys like posts/% work correctly without breaking cache consistency.

@L33Z22L11 L33Z22L11 requested a review from danielroe as a code owner October 17, 2025 14:35
@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@coderabbitai
Copy link

coderabbitai bot commented Oct 17, 2025

Walkthrough

In packages/nitro-server/src/runtime/utils/cache-driver.js the key-normalisation implementation was replaced: an explicit crypto import was added and the inline arrow function became a named normalizeFsKey that sanitises the input by replacing disallowed characters with underscores, derives a 20-character sanitized prefix from the original key, computes a SHA-256 hash of the original key, and returns a prefix-hash string. Filesystem-backed storage now uses normalizeFsKey(key) while in-memory LRU paths continue to use the original key. No exported or public declarations were changed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "fix(nuxt): fallback on failed key decoding in cache driver" directly corresponds to the main change in the changeset. According to the raw summary, the pull request updates the normalizeFsKey function to handle key normalization, and the PR objectives confirm the fix involves adding a try/catch fallback around decodeURIComponent to handle failed key decoding. The title is concise, follows conventional commits format, and clearly communicates the purpose of the fix to team members reviewing the commit history.
Description Check ✅ Passed The pull request description is directly related to the changeset and provides substantive information about the problem being addressed. It explains the root cause (malformed URI errors when keys contain literal % characters), provides clear reproduction steps, and describes the proposed solution (wrapping decodeURIComponent in a try/catch block with a fallback mechanism). The description includes sufficient context linking to the introduced bug and demonstrates a clear understanding of the issue and fix.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/nitro-server/src/runtime/utils/cache-driver.js (1)

10-18: Consider catching specifically URIError and including error details in the warning.

The implementation correctly handles the decoding failure scenario. However, the catch block is generic and the warning doesn't include error details, which could aid debugging.

Apply this diff to improve error handling:

 function normalizeFsKey(item) {
   const normalized = item.replaceAll(':', '_')
   try {
     return decodeURIComponent(normalized)
-  } catch (error) {
-    console.warn(`Failed to decode key "${item}", using "${normalized}" instead.`)
+  } catch (error) {
+    if (error instanceof URIError) {
+      console.warn(`Failed to decode key "${item}" (${error.message}), using "${normalized}" instead.`)
+    } else {
+      throw error
+    }
     return normalized
   }
 }

This ensures only expected URI decoding errors are caught whilst re-throwing unexpected errors, and provides the error message for better diagnostics.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39dd9b6 and f90f988.

📒 Files selected for processing (1)
  • packages/nitro-server/src/runtime/utils/cache-driver.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: code

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 17, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@33505

nuxt

npm i https://pkg.pr.new/nuxt@33505

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@33505

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@33505

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@33505

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@33505

commit: 81d4f82

@codspeed-hq
Copy link

codspeed-hq bot commented Oct 17, 2025

CodSpeed Performance Report

Merging #33505 will not alter performance

Comparing L33Z22L11:fix/cache-driver-key (81d4f82) with main (b7ed1d3)

Summary

✅ 10 untouched

*/
const normalizeFsKey = item => decodeURIComponent(item.replaceAll(':', '_'))
function normalizeFsKey(item) {
const normalized = item.replaceAll(':', '_')
Copy link
Member

Choose a reason for hiding this comment

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

can we choose a better normalisation method instead? maybe just use something like base64 encoding?

* @param {string} item
*/
const normalizeFsKey = item => decodeURIComponent(item.replaceAll(':', '_'))
function normalizeFsKey(item) {
Copy link
Member

Choose a reason for hiding this comment

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

maybe just:

import { createHash } from 'node:crypto'

const normalizeFsKey = item => createHash('sha256').update(item).digest('hex')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now use hashes, limit length, and preserve readability. defineURIComponent is not used because of the need for additional error handling and concerns about file system support for special characters.

Copy link
Member

Choose a reason for hiding this comment

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

is there a reason you're using sha1 rather than sha256?

and one more question: a pure hash would be 64 chars, and this is 41 chars, which isn't that much shorter but increases the risk of collisions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. I initially thought SHA-1 would be faster, but after running some benchmarks, SHA-256 actually turned out to be faster on my machine. I've switched to using the full hash now.

Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/nitro-server/src/runtime/utils/cache-driver.js (1)

11-16: Excellent solution – cleaner than the try/catch approach.

The hybrid prefix-hash format elegantly solves the original issue with literal % characters whilst maintaining filesystem safety and readability. The regex correctly sanitises problematic characters, and the full SHA-256 hash (64 chars) provides negligible collision risk, addressing concerns raised in previous reviews.

Optionally, consider adding JSDoc to document the normalisation format for future maintainers:

 /**
  * @param {string} item
+ * Normalizes cache keys for filesystem storage by combining a sanitized
+ * 20-character prefix with a SHA-256 hash for uniqueness.
+ * Format: "prefix-hash" (e.g., "posts_-abc123...")
+ * @returns {string} Normalized key safe for filesystem use
  */
 function normalizeFsKey (item) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 621a269 and 81d4f82.

📒 Files selected for processing (1)
  • packages/nitro-server/src/runtime/utils/cache-driver.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: code
🔇 Additional comments (2)
packages/nitro-server/src/runtime/utils/cache-driver.js (2)

3-3: LGTM: Crypto import is correct.

The import is necessary for SHA-256 hashing in the normalisation function.


29-29: Correct usage of normalisation for filesystem operations—no issues found.

Code inspection confirms the implementation correctly applies normalizeFsKey only to filesystem operations whilst preserving the original key for the in-memory LRU cache. The normalizeFsKey function replaces non-alphanumeric characters (including %) with underscores and appends a SHA256 hash of the original key, ensuring filesystem compatibility without losing information. This approach resolves issues with keys containing special characters like %.

The usage pattern across setItem, hasItem, and getItem methods is consistent: LRU operations use the unmodified key, whilst filesystem operations use the normalised key.

Copy link
Member

@danielroe danielroe left a comment

Choose a reason for hiding this comment

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

thank you

@danielroe danielroe merged commit ca3347b into nuxt:main Oct 21, 2025
100 of 101 checks passed
This was referenced Oct 21, 2025
@github-actions github-actions bot mentioned this pull request Oct 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants