|
2 | 2 | MAX_RETRIES_PER_MESSAGE, |
3 | 3 | RETRY_BACKOFF_BASE_DELAY_MS, |
4 | 4 | RETRY_BACKOFF_MAX_DELAY_MS, |
| 5 | + isPaymentRequiredError, |
| 6 | + ErrorCodes, |
5 | 7 | } from '@codebuff/sdk' |
6 | 8 | import { useQueryClient } from '@tanstack/react-query' |
7 | 9 | import { has, isEqual } from 'lodash' |
@@ -1769,15 +1771,53 @@ export const useSendMessage = ({ |
1769 | 1771 | }) |
1770 | 1772 |
|
1771 | 1773 | if (!runState.output || runState.output.type === 'error') { |
1772 | | - logger.warn( |
1773 | | - { |
1774 | | - errorMessage: |
1775 | | - runState.output?.type === 'error' |
1776 | | - ? runState.output.message |
1777 | | - : 'No output from agent run', |
1778 | | - }, |
1779 | | - 'Agent run failed', |
1780 | | - ) |
| 1774 | + const errorOutput = runState.output?.type === 'error' ? runState.output : null |
| 1775 | + const errorMessage = errorOutput?.message ?? 'No output from agent run' |
| 1776 | + |
| 1777 | + logger.warn({ errorMessage, errorCode: errorOutput?.errorCode }, 'Agent run failed') |
| 1778 | + |
| 1779 | + // Check if this is an out-of-credits error using the error code |
| 1780 | + const isOutOfCredits = errorOutput?.errorCode === ErrorCodes.PAYMENT_REQUIRED |
| 1781 | + |
| 1782 | + if (isOutOfCredits) { |
| 1783 | + const appUrl = process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com' |
| 1784 | + const paymentErrorMessage = |
| 1785 | + errorOutput?.message ?? |
| 1786 | + `Out of credits. Please add credits at ${appUrl}/usage` |
| 1787 | + applyMessageUpdate((prev) => |
| 1788 | + prev.map((msg) => { |
| 1789 | + if (msg.id !== aiMessageId) return msg |
| 1790 | + return { |
| 1791 | + ...msg, |
| 1792 | + content: paymentErrorMessage, |
| 1793 | + blocks: undefined, // Clear blocks so content renders |
| 1794 | + isComplete: true, |
| 1795 | + } |
| 1796 | + }), |
| 1797 | + ) |
| 1798 | + // Show the usage banner so user can see their balance and renewal date |
| 1799 | + useChatStore.getState().setInputMode('usage') |
| 1800 | + // Refresh usage data to show current state |
| 1801 | + queryClient.invalidateQueries({ queryKey: usageQueryKeys.current() }) |
| 1802 | + } else { |
| 1803 | + // Generic error - display the error message directly from SDK |
| 1804 | + applyMessageUpdate((prev) => |
| 1805 | + prev.map((msg) => { |
| 1806 | + if (msg.id !== aiMessageId) return msg |
| 1807 | + return { |
| 1808 | + ...msg, |
| 1809 | + content: `**Error:** ${errorMessage}`, |
| 1810 | + blocks: undefined, // Clear blocks so content renders |
| 1811 | + isComplete: true, |
| 1812 | + } |
| 1813 | + }), |
| 1814 | + ) |
| 1815 | + } |
| 1816 | + |
| 1817 | + setStreamStatus('idle') |
| 1818 | + setCanProcessQueue(true) |
| 1819 | + updateChainInProgress(false) |
| 1820 | + timerController.stop('error') |
1781 | 1821 | return |
1782 | 1822 | } |
1783 | 1823 |
|
@@ -1835,6 +1875,35 @@ export const useSendMessage = ({ |
1835 | 1875 |
|
1836 | 1876 | const errorMessage = |
1837 | 1877 | error instanceof Error ? error.message : 'Unknown error occurred' |
| 1878 | + |
| 1879 | + // Handle payment required (out of credits) specially |
| 1880 | + if (isPaymentRequiredError(error)) { |
| 1881 | + const appUrl = process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com' |
| 1882 | + const paymentErrorMessage = |
| 1883 | + error instanceof Error && error.message |
| 1884 | + ? error.message |
| 1885 | + : `Out of credits. Please add credits at ${appUrl}/usage` |
| 1886 | + |
| 1887 | + applyMessageUpdate((prev) => |
| 1888 | + prev.map((msg) => { |
| 1889 | + if (msg.id !== aiMessageId) { |
| 1890 | + return msg |
| 1891 | + } |
| 1892 | + return { |
| 1893 | + ...msg, |
| 1894 | + content: paymentErrorMessage, |
| 1895 | + blocks: undefined, // Clear blocks so content renders |
| 1896 | + isComplete: true, |
| 1897 | + } |
| 1898 | + }), |
| 1899 | + ) |
| 1900 | + // Show the usage banner so user can see their balance and renewal date |
| 1901 | + useChatStore.getState().setInputMode('usage') |
| 1902 | + // Refresh usage data to show current state |
| 1903 | + queryClient.invalidateQueries({ queryKey: usageQueryKeys.current() }) |
| 1904 | + return |
| 1905 | + } |
| 1906 | + |
1838 | 1907 | applyMessageUpdate((prev) => |
1839 | 1908 | prev.map((msg) => { |
1840 | 1909 | if (msg.id !== aiMessageId) { |
|
0 commit comments