Troubleshooting
API errors by HTTP status
Every error response is JSON. Most endpoints return { "status": false, "error": "<message>" }. The Shareholders endpoint returns { "error": "<label>", "message": "<message>" } for its 400, 403 and 500 responses.
| Status | Returned by | What it means | What to check |
|---|---|---|---|
400 | All endpoints | The request was rejected before processing. The error text names the field or rule. | Token: client_id and client_secret are both present. Events: recordDate and pastRecordDate are YYYY-MM-DD, pastRecordDate is not a future date, limit is 25 or less, at most 15 CUSIPs, ISINs or tickers. Event details and Voting URL: eventId and brokerId are 24-character hex IDs. Voting URL: accountNo and brokerId are both present, and you are not passing email. Shareholders and Trades: the field named in the message. |
401 | All endpoints | On the token endpoint, the credentials were rejected. On every other endpoint, the Bearer token is missing, expired or invalid. The body is always Authorization header is required, whatever the cause. | You are sending Authorization: Bearer <token>. The token was issued for the environment you are calling. You refresh it before expires_in elapses. |
403 | Shareholders, Voting URL | Shareholders: the reporting deadline for this event has passed. Voting URL: brokerId is not the broker the shareholder was submitted under. | Shareholders: check shareholderReportingDeadline on the event and submit on the evening of the record date. Voting URL: use the same brokerId you used in the shareholders request. |
404 | Event details, Voting URL | Event details: no event with that ID. Voting URL: no event with that ID, the event is not a Proxy event, or accountNo was not submitted for this event. | The eventId comes from the Events list. The account number is exactly the value you submitted. The event's eventType is Proxy. |
423 | Voting URL | Voting is not open. Either final documents are not confirmed yet ("Voting URL Not Active") or the voting period has ended. | Call Event details first and only show "Vote Now" when votingLinksActive is true. |
429 | All endpoints | The AWS API gateway rate limit was hit: 5,000 requests per 5 minutes per IP address across all /broker/v1 routes. | Back off and retry. Spread bulk work across the window. |
500 | All endpoints | An unexpected server error. | Retry with backoff. If it persists, contact the VOTR team with the endpoint, timestamp and request body. |
Authentication Issues
"Invalid client credentials" error
Symptoms
- OAuth API returns
401or403error. - Authentication fails.
Solution
- Verify Client ID and Client Secret are correct.
- Ensure no extra spaces or characters.
- Confirm credentials are stored securely on your backend.
- Verify you're using the correct environment URL.
- Contact the VOTR team to verify credentials.
Token expiration errors
Symptoms
- API calls work initially but start failing with
401after a while. Access token has expirederror messages.
Solution
Tokens expire after the number of seconds returned in expires_in. Cache the token and refresh it before that elapses:
const getValidToken = async () => {
const now = Date.now();
if (cachedToken && cachedToken.expires > now) {
return cachedToken.access_token;
}
const newToken = await getOAuthToken();
cachedToken = {
access_token: newToken.access_token,
expires: now + newToken.expires_in * 1000,
};
return cachedToken.access_token;
};
API Issues
"Account number not found for this event" error
Symptoms
- The voting URL request returns
404even though the shareholder was submitted. - The voting URL request returns
403"Shareholder does not belong to the specified broker".
Solution
- Check that
accountNois exactly the value you submitted in the shareholders request for this event. The lookup is an exact match. - Check that
brokerIdis the same broker ID you submitted the shareholder under. A mismatch returns403. - Check the
eventIdis the event the shareholder was submitted for. - Confirm the shareholders submission for this event was accepted (
202) before the reporting deadline.
Network request failing
Symptoms
- API calls timeout or fail.
- Connection errors.
Solution
- Check internet connectivity.
- Verify the base URL — Development
https://api-stage.govotr.com/broker/v1, Productionhttps://server.govotr.com/broker/v1. - Ensure proper headers are included.
- Implement retry logic for network failures (see code below).
const fetchWithRetry = async (url: string, options: any, retries = 3) => {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
return response;
} catch (error) {
if (i === retries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
}
}
};
SDK Installation Issues
"Module not found" error after SDK installation
# Clear npm cache and reinstall
npm cache clean --force
rm -rf node_modules package-lock.json
npm install
npm install ./govotr-vote-react-native-1.0.0.tgz --legacy-peer-deps
Peer dependency warnings
The --legacy-peer-deps flag is required for this package. This is normal and expected.
TypeScript errors
The SDK ships its own type definitions, which reference types from react and react-native. React Native 0.71 and later bundles its own types, so do not install @types/react-native. The only type package you need is @types/react, which every TypeScript React Native project already has. If it is missing:
npm install @types/react --save-dev
SDK Component Issues
WebView not opening or blank screen
Symptoms
- Button press doesn't open voting interface.
- Blank white screen appears.
- Modal opens but no content.
Solution
- Check that the voting URL is valid and not empty.
- Verify the URL format is correct (should contain ID).
- Test with a known working URL.
- Check the console for JavaScript errors.
- Ensure WebView has proper permissions in your app.
console.log("Voting URL:", votingUrl);
console.log("URL is valid:", votingUrl && votingUrl.length > 0);
Button not responding to press
Symptoms
- Button appears but doesn't respond to taps.
- No console logs from
onPresshandler.
Solution
- Check whether the button is disabled:
isDisabled={false}. - Verify
onPressis defined. - Check for overlapping views that may intercept touches.
- Ensure button has proper dimensions.
<VoteNowButton
URL={votingUrl}
isDisabled={false}
onPress={() => console.log("Button pressed")}
buttonStyle={{
minHeight: 44,
minWidth: 200,
}}
/>
Modal / WebView crashes or freezes
Symptoms
- App crashes when voting modal opens.
- WebView becomes unresponsive.
- Memory issues during voting.
Solution
- Implement proper cleanup on component unmount.
- Add error boundaries around the SDK component.
- Monitor memory usage during development.
useEffect(() => {
return () => {
setVotingUrl("");
};
}, []);
Error Handling Patterns
SDK error types
<VoteNowButton
URL={votingUrl}
onError={(error) => {
console.error("SDK Error:", error);
let userMessage = "An unexpected error occurred during voting.";
if (error.includes("network")) {
userMessage = "Network connection lost. Please check your internet connection.";
} else if (error.includes("timeout")) {
userMessage = "Voting session timed out. Please try again.";
} else if (error.includes("permission")) {
userMessage = "You do not have permission to access this voting session.";
} else if (error.includes("invalid_token")) {
userMessage = "Your voting session has expired. Please try again.";
} else if (error.includes("already_voted")) {
userMessage = "You have already submitted your vote for this event.";
}
Alert.alert("Voting Error", userMessage);
setVotingUrl("");
}}
/>
API error handling
const handleApiError = (error: any, context: string) => {
console.error(`${context} error:`, error);
if (error.response) {
const status = error.response.status;
const message = error.response.data?.message || "Unknown error";
switch (status) {
case 401:
return "Authentication failed. Please check your credentials.";
case 403:
return "You don't have permission to perform this action.";
case 404:
return "The requested resource was not found.";
case 500:
return "Server error. Please try again later.";
default:
return `Error ${status}: ${message}`;
}
} else if (error.request) {
return "Network error. Please check your internet connection.";
} else {
return `Unexpected error: ${error.message}`;
}
};
Performance Tips
Token caching
const tokenCache = { token: null, expires: 0 };
const getCachedToken = async () => {
if (tokenCache.token && Date.now() < tokenCache.expires) {
return tokenCache.token;
}
const response = await getOAuthToken();
tokenCache.token = response.access_token;
tokenCache.expires = Date.now() + response.expires_in * 1000;
return tokenCache.token;
};
Loading states
const [isLoading, setIsLoading] = useState(false);
<VoteNowButton
URL={votingUrl}
isDisabled={isLoading || !votingUrl}
label={isLoading ? "Loading..." : "Vote Now"}
/>;
Getting Additional Help
Development Resources
Contact Support
When contacting support, please include:
Error messages (exact text).
Console logs (if available).
SDK version (
1.0.0).Platform (React Native / React).
Environment (Development / Production).
Steps to reproduce the issue.
Debug Checklist
Before reporting issues, verify:
If you're still experiencing issues, contact the VOTR team with your specific error details and logs.