diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx
index fee93f68a6..6ac9288923 100644
--- a/webview-ui/src/components/chat/ChatRow.tsx
+++ b/webview-ui/src/components/chat/ChatRow.tsx
@@ -25,6 +25,7 @@ import { ReasoningBlock } from "./ReasoningBlock"
import Thumbnails from "../common/Thumbnails"
import ImageBlock from "../common/ImageBlock"
import ErrorRow from "./ErrorRow"
+import { RateLimitCountdown } from "./RateLimitCountdown"
import McpResourceRow from "../mcp/McpResourceRow"
@@ -1087,6 +1088,14 @@ export const ChatRowContent = ({
>
)
case "api_req_retry_delayed":
+ // Check if this is user-configured rate limiting (not an API error)
+ if (message.text?.startsWith("Rate limiting for")) {
+ // Extract countdown from message text: "Rate limiting for X seconds..."
+ const rateLimitMatch = message.text.match(/Rate limiting for (\d+) seconds/)
+ const countdown = rateLimitMatch ? parseInt(rateLimitMatch[1], 10) : 0
+ return
+ }
+
let body = t(`chat:apiRequest.failed`)
let retryInfo, rawError, code, docsURL
if (message.text !== undefined) {
diff --git a/webview-ui/src/components/chat/RateLimitCountdown.tsx b/webview-ui/src/components/chat/RateLimitCountdown.tsx
new file mode 100644
index 0000000000..e9ae99db56
--- /dev/null
+++ b/webview-ui/src/components/chat/RateLimitCountdown.tsx
@@ -0,0 +1,30 @@
+import React, { memo } from "react"
+import { useTranslation } from "react-i18next"
+import { Timer } from "lucide-react"
+
+export interface RateLimitCountdownProps {
+ seconds: number
+}
+
+/**
+ * Displays a user-configured rate limiting countdown as an informational message.
+ * This is NOT an error state - it's expected behavior based on user settings.
+ *
+ * Uses neutral/informational styling instead of error styling.
+ */
+export const RateLimitCountdown = memo(({ seconds }: RateLimitCountdownProps) => {
+ const { t } = useTranslation()
+
+ return (
+
+
+
+ {t("chat:rateLimit.countdown", { seconds, defaultValue: `Rate limiting: ${seconds}s` })}
+
+
+ )
+})
+
+RateLimitCountdown.displayName = "RateLimitCountdown"
+
+export default RateLimitCountdown
diff --git a/webview-ui/src/components/chat/__tests__/RateLimitCountdown.spec.tsx b/webview-ui/src/components/chat/__tests__/RateLimitCountdown.spec.tsx
new file mode 100644
index 0000000000..883dbcc846
--- /dev/null
+++ b/webview-ui/src/components/chat/__tests__/RateLimitCountdown.spec.tsx
@@ -0,0 +1,54 @@
+import React from "react"
+
+import { render, screen } from "@/utils/test-utils"
+
+import { RateLimitCountdown } from "../RateLimitCountdown"
+
+// Mock i18n
+vi.mock("react-i18next", () => ({
+ useTranslation: () => ({
+ t: (key: string, params?: { seconds?: number }) => {
+ if (key === "chat:rateLimit.countdown") {
+ return `Rate limiting: ${params?.seconds}s`
+ }
+ return key
+ },
+ }),
+ initReactI18next: {
+ type: "3rdParty",
+ init: vi.fn(),
+ },
+}))
+
+describe("RateLimitCountdown", () => {
+ it("renders with countdown seconds", () => {
+ render()
+
+ expect(screen.getByText("Rate limiting: 5s")).toBeInTheDocument()
+ })
+
+ it("renders with zero seconds", () => {
+ render()
+
+ expect(screen.getByText("Rate limiting: 0s")).toBeInTheDocument()
+ })
+
+ it("uses informational styling (not error styling)", () => {
+ const { container } = render()
+
+ // Check that the component has the expected informational styling class
+ const rootDiv = container.firstChild as HTMLElement
+ expect(rootDiv).toHaveClass("text-vscode-descriptionForeground")
+
+ // Verify it does NOT have error-related styling
+ expect(rootDiv).not.toHaveClass("text-vscode-errorForeground")
+ })
+
+ it("renders the Timer icon", () => {
+ const { container } = render()
+
+ // Lucide icons render as SVG elements
+ const svgIcon = container.querySelector("svg")
+ expect(svgIcon).toBeInTheDocument()
+ })
+})
diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json
index 2d71f43b2b..cd48380c73 100644
--- a/webview-ui/src/i18n/locales/en/chat.json
+++ b/webview-ui/src/i18n/locales/en/chat.json
@@ -470,5 +470,8 @@
"updated": "Updated the to-do list",
"completed": "Completed",
"started": "Started"
+ },
+ "rateLimit": {
+ "countdown": "Rate limiting: {{seconds}}s"
}
}