Install
$ agentstack add skill-bradtaylorsf-alphaagent-team-defensive-api-handling ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Defensive API Handling
Purpose: Safely handle API responses to prevent crashes
When to Use: Any time you call an API endpoint (in tests or production code)
Never Assume API Returns JSON
APIs can return:
- JSON (expected)
- HTML error pages
- Empty responses
- Malformed JSON
- Non-200 status codes
Your code must handle ALL of these safely.
Pattern 1: Safe JSON Parsing
async function safeJsonParse(response: Response): Promise {
// 1. Check status code
if (!response.ok) {
console.warn(`API returned ${response.status} ${response.statusText}`);
return null;
}
// 2. Check content type
const contentType = response.headers.get('content-type');
if (!contentType?.includes('application/json')) {
console.warn(`Expected JSON, got ${contentType}`);
const text = await response.text();
console.warn(`Response body: ${text.slice(0, 200)}`);
return null;
}
// 3. Try to parse
try {
return await response.json();
} catch (error) {
console.warn('Failed to parse JSON:', error.message);
return null;
}
}
Pattern 2: Fetch with Timeout
async function fetchWithTimeout(
url: string,
options: RequestInit = {},
timeoutMs: number = 10000
): Promise {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
return response;
} finally {
clearTimeout(timeout);
}
}
Pattern 3: Retry Logic
async function fetchWithRetry(
url: string,
options: RequestInit = {},
maxRetries: number = 3,
delayMs: number = 1000
): Promise {
for (let attempt = 1; attempt = 500 && attempt setTimeout(resolve, delayMs * attempt));
continue;
}
return response;
} catch (error) {
if (attempt === maxRetries) {
console.error(`All ${maxRetries} attempts failed:`, error.message);
return null;
}
await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
}
}
return null;
}
Pattern 4: Type-Safe API Responses
// Define expected response types
interface ApiResponse {
data?: T;
error?: string;
}
// Type guard function
function isApiResponse(
value: unknown,
dataGuard: (v: unknown) => v is T
): value is ApiResponse {
if (typeof value !== 'object' || value === null) return false;
const obj = value as Record;
if ('error' in obj && typeof obj.error !== 'string') return false;
if ('data' in obj && !dataGuard(obj.data)) return false;
return true;
}
// Safe API call with type checking
async function safeApiCall(
url: string,
dataGuard: (v: unknown) => v is T
): Promise {
const response = await fetchWithRetry(url);
if (!response) return null;
const json = await safeJsonParse(response);
if (!json) return null;
if (!isApiResponse(json, dataGuard)) {
console.warn('Unexpected response structure:', json);
return null;
}
if (json.error) {
console.warn('API returned error:', json.error);
return null;
}
return json.data ?? null;
}
Pattern 5: Error Boundary for API Calls
async function withApiErrorBoundary(
operation: () => Promise,
context: string,
fallback: T
): Promise {
try {
return await operation();
} catch (error) {
console.error(`[${context}] API error:`, error.message);
// Log additional context
if (error instanceof TypeError && error.message.includes('fetch')) {
console.error(`Network error - check connectivity`);
}
return fallback;
}
}
// Usage
const users = await withApiErrorBoundary(
() => fetchUsers(),
'fetchUsers',
[] // fallback to empty array
);
Anti-Patterns to Avoid
// ❌ BAD: Assumes JSON, no error handling
const data = await fetch('/api/users').then(r => r.json());
// ❌ BAD: No timeout
const response = await fetch('/api/slow-endpoint');
// ❌ BAD: Trusts response structure
const users = data.users.map(u => u.name);
// ❌ BAD: Silent failures
try {
return await fetch('/api/users').then(r => r.json());
} catch {
return []; // Caller doesn't know it failed
}
Success Criteria
- Zero
SyntaxError: Unexpected token '<'errors - Zero
Cannot read property of undefinederrors - Cleanup hooks never cause test suite to crash
- Tests skip gracefully when API unavailable
- All API calls have timeouts
- Errors are logged with context
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: bradtaylorsf
- Source: bradtaylorsf/alphaagent-team
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.