vercel/next.js · error · ReadonlyURLSearchParamsError
Method unavailable on `ReadonlyURLSearchParams`. Read more:
Error message
Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams
What it means
`useSearchParams()` from `next/navigation` returns a `ReadonlyURLSearchParams` instance that overrides `append()` to throw. The App Router deliberately makes search params read-only on the client because URL state should be managed through the router (router.push/replace), not by mutating the params object directly.
Source
Thrown at packages/next/src/client/components/readonly-url-search-params.ts:23
*/
/** @internal */
class ReadonlyURLSearchParamsError extends Error {
constructor() {
super(
'Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams'
)
}
}
/**
* A read-only version of URLSearchParams that throws errors when mutation methods are called.
* This ensures that the URLSearchParams returned by useSearchParams() cannot be mutated.
*/
export class ReadonlyURLSearchParams extends URLSearchParams {
/** @deprecated Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams */
append() {
throw new ReadonlyURLSearchParamsError()
}
/** @deprecated Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams */
delete() {
throw new ReadonlyURLSearchParamsError()
}
/** @deprecated Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams */
set() {
throw new ReadonlyURLSearchParamsError()
}
/** @deprecated Method unavailable on `ReadonlyURLSearchParams`. Read more: https://nextjs.org/docs/app/api-reference/functions/use-search-params#updating-searchparams */
sort() {
throw new ReadonlyURLSearchParamsError()
}
}
View on GitHub (pinned to 0ae8c72462)
Solutions
- Construct a new mutable URLSearchParams from the readonly values, mutate it, then navigate: `const p = new URLSearchParams(searchParams.toString()); p.append('key', 'val'); router.push('?' + p.toString())`.
- Use router.push or router.replace with the full updated query string.
- Use the `usePathname` + `useRouter` pattern to build the target URL.
Example fix
// before — throws
const searchParams = useSearchParams()
searchParams.append('page', '2')
// after — build a new URL and navigate
const searchParams = useSearchParams()
const router = useRouter()
const params = new URLSearchParams(searchParams.toString())
params.append('page', '2')
router.push(`?${params.toString()}`) Defensive patterns
Strategy: type-guard
Validate before calling
import { useSearchParams } from 'next/navigation'
// Use a writable copy for mutations
function useMutableSearchParams(): URLSearchParams {
const readonly = useSearchParams()
return new URLSearchParams(readonly.toString())
} Type guard
import { ReadonlyURLSearchParams } from 'next/navigation'
function isReadonlyURLSearchParams(
v: unknown
): v is ReadonlyURLSearchParams {
return v instanceof ReadonlyURLSearchParams
} Prevention
- Never call mutation methods (.append/.delete/.set/.sort) on useSearchParams() return value.
- If you need to mutate, create a new URLSearchParams from searchParams.toString() first.
- Use router.push/replace to apply URL changes, not direct param mutation.
When it happens
Trigger: Calling `.append()` on the value returned by `useSearchParams()`. The class extends URLSearchParams but overrides the mutation method to throw a `ReadonlyURLSearchParamsError`.
Common situations: Migrating from Pages Router where URLSearchParams was fully mutable; using a URL manipulation helper that expects to call .append(); adding a query param to the current URL in an event handler.
Related errors
- Cannot prefetch '${href}' because it cannot be converted to
- [inspect] ${route}: not an App Router document (no __next_f)
- Default export is missing in {resource_path}
- id property is required for every item returned from generat
- id property is required for every item returned from generat
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/3a5d6291fb25c165.
Report an issue: GitHub.