Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions components/ApiKeyInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ export function ApiKeyInput({
return false
}

// Validate key format - OpenRouter keys must start with "sk-or-v1-"
if (!keyToTest.startsWith('sk-or-v1-')) {
setTestResult({
isValid: false,
error: 'Invalid API key format. OpenRouter keys must start with "sk-or-v1-"'
})
return false
}
Copy link

Choose a reason for hiding this comment

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

Bug: Stale Invalid API Key Causes Inconsistent State

Early-return on invalid key format sets an error but does not clear a stored invalid API key or notify the parent via onApiKeyValidated(false), unlike other failure paths. This can leave a stale invalid key in sessionStorage and skip parent state updates (especially when autoTest triggers testApiKey), causing inconsistent UI/state compared to other error cases.

Fix in Cursor Fix in Web


setIsTestingKey(true)
setTestResult(null)
onLoadingChange?.(true)
Expand All @@ -67,19 +76,34 @@ export function ApiKeyInput({
} else {
setTestResult({
isValid: false,
error: 'Invalid API key'
error: 'Invalid API key - could not connect to OpenRouter. Please check your API key.'
})
// Clear invalid key from sessionStorage
clearAPIKey()
onApiKeyValidated?.(false, keyToTest)
return false
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to test API key'
let errorMessage = 'Failed to test API key'

if (error instanceof Error) {
if (error.message.includes('401') || error.message.includes('403')) {
errorMessage = 'Invalid API key - authentication failed. Please check your OpenRouter API key.'
} else if (error.message.includes('429')) {
errorMessage = 'Rate limit exceeded. Please wait before trying again.'
} else if (error.message.includes('Network') || error.message.includes('fetch')) {
errorMessage = 'Network error - could not connect to OpenRouter. Please check your internet connection.'
} else {
errorMessage = `Validation failed: ${error.message}`
}
}

setTestResult({
isValid: false,
error: `Validation failed: ${errorMessage}`
error: errorMessage
})
// Clear invalid key from sessionStorage
clearAPIKey()
onApiKeyValidated?.(false)
return false
} finally {
Expand Down Expand Up @@ -112,11 +136,11 @@ export function ApiKeyInput({
return
}

// Validate key format
if (!trimmedValue.startsWith('sk-or-') && !trimmedValue.startsWith('sk-')) {
// Validate key format - OpenRouter keys must start with "sk-or-v1-"
if (!trimmedValue.startsWith('sk-or-v1-')) {
setTestResult({
isValid: false,
error: 'OpenRouter API keys should start with "sk-or-" or "sk-"'
error: 'OpenRouter API keys must start with "sk-or-v1-"'
})
return
}
Expand Down Expand Up @@ -146,11 +170,11 @@ export function ApiKeyInput({
return
}

// Validate key format
if (!keyToTest.startsWith('sk-or-') && !keyToTest.startsWith('sk-')) {
// Validate key format - OpenRouter keys must start with "sk-or-v1-"
if (!keyToTest.startsWith('sk-or-v1-')) {
setTestResult({
isValid: false,
error: 'Invalid API key format'
error: 'Invalid API key format. OpenRouter keys must start with "sk-or-v1-"'
})
onApiKeyValidated?.(false, keyToTest)
return
Expand Down Expand Up @@ -216,7 +240,7 @@ export function ApiKeyInput({
<div className="relative">
<Input
type={showKey ? 'text' : 'password'}
placeholder={apiKey ? "Your API key is saved and validated" : "sk-or-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
placeholder={apiKey ? "Your API key is saved and validated" : "sk-or-v1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
value={inputValue}
onChange={handleInputChange}
onKeyPress={handleKeyPress}
Expand Down
26 changes: 23 additions & 3 deletions components/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function ModelSelector({
filterByCapability = 'all',
className = ''
}: ModelSelectorProps) {
const { value: apiKey } = useSimpleApiKeyStorage()
const { value: apiKey, isValidFormat: isApiKeyValidFormat } = useSimpleApiKeyStorage()
const [models, setModels] = useState<OpenRouterModel[]>([])
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
Expand All @@ -66,6 +66,13 @@ export function ModelSelector({
return
}

// Check if API key has valid format
if (!isApiKeyValidFormat) {
setError('Invalid API key format. OpenRouter API keys must start with "sk-or-v1-"')
onError?.()
return
}

setIsLoading(true)
setError(null)
onLoadingChange?.(true)
Expand All @@ -81,14 +88,27 @@ export function ModelSelector({
setModels(fetchedModels)
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to load models'
let errorMessage = 'Failed to load models'

if (err instanceof Error) {
if (err.message.includes('401') || err.message.includes('403')) {
errorMessage = 'Invalid API key - authentication failed. Please check your OpenRouter API key.'
} else if (err.message.includes('429')) {
errorMessage = 'Rate limit exceeded. Please wait before trying again.'
} else if (err.message.includes('Network') || err.message.includes('fetch')) {
errorMessage = 'Network error - could not connect to OpenRouter. Please check your internet connection.'
} else {
errorMessage = err.message
}
}

setError(errorMessage)
onError?.()
} finally {
setIsLoading(false)
onLoadingChange?.(false)
}
}, [apiKey])
}, [apiKey, isApiKeyValidFormat, onError, onLoadingChange])

// Load models when component mounts or API key changes
useEffect(() => {
Expand Down
5 changes: 3 additions & 2 deletions hooks/useSimpleApiKeyStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,14 @@ export function useSimpleApiKeyStorage() {
}, [])

// Simple validation check
const isValidFormat = apiKey ? apiKey.startsWith('sk-or-') || apiKey.startsWith('sk-') : false
const isValidFormat = apiKey ? apiKey.startsWith('sk-or-v1-') : false
const hasValidKey = Boolean(apiKey && isValidFormat && isValidated)

return {
value: apiKey,
hasValidKey,
setAPIKey,
clearAPIKey
clearAPIKey,
isValidFormat
}
}
Loading