validate-inputs-first
$npx skills add blunotech-dev/agents --skill validate-inputs-firstValidate that function inputs match the expected shape. Use when debugging unexpected behavior, null errors, type issues, or incorrect arguments.
| name | description | category |
|---|---|---|
| validate-inputs-first | Validate that function inputs match the expected shape. Use when debugging unexpected behavior, null errors, type issues, or incorrect arguments. | Debugging |
Validate Inputs First
Instructions
Before investigating internal logic, always verify that the function receives the correct inputs. A large percentage of bugs are caused by unexpected input data—not broken logic.
Step 1: Catalog Expected Inputs
For the function under investigation, document what each input should be:
EXPECTED INPUT CONTRACT
━━━━━━━━━━━━━━━━━━━━━━
Function: createUser(userData, options)
| Parameter | Type | Required | Shape / Constraints |
|------------|--------|----------|--------------------------------|
| userData | Object | Yes | { name: string, email: string, |
| | | | age: number (>= 0) } |
| options | Object | No | { sendEmail: boolean, |
| | | | role: "admin"|"user" } |
Determine the expected contract from:
- Type annotations / TypeScript interfaces / JSDoc
- Function documentation or comments
- Validation logic within the function
- How callers invoke the function (look at actual call sites)
- Test cases that exercise the function
Step 2: Inspect Actual Inputs
Determine what is actually being passed in:
- Trace upstream: Follow the data from its origin to the function call.
- Check transformations: Identify any mapping, filtering, or formatting applied before the call.
- Look for mutations: Has the data been modified by another function between creation and use?
Document actual vs. expected:
INPUT COMPARISON
━━━━━━━━━━━━━━━━
Parameter: userData
Expected: Actual:
{ {
name: "Alice", name: "Alice",
email: "a@b.com", email: null, ← MISMATCH
age: 30 age: "30" ← WRONG TYPE
} }
Step 3: Identify Mismatches
For each input, check:
- Presence: Is the parameter provided at all? (not
undefined) - Type: Is it the correct data type? (
stringvsnumbervsobject) - Shape: Does the object/array have the expected structure and keys?
- Values: Are the values within valid ranges? (non-empty, positive, valid enum)
- Encoding: Is the data in the expected format? (UTF-8, URL-encoded, base64)
- Staleness: Is the data current, or could it be stale/cached?
Common mismatch patterns:
| Pattern | Example | Usual Cause |
|---|---|---|
undefined parameter | fn(a, undefined, c) | Destructuring error, missing prop |
| String instead of number | age: "30" | Form input, query params, JSON parsing |
| Null instead of object | user: null | Failed API call, missing DB record |
| Array instead of single item | items: [{...}] vs item: {...} | API returns list, code expects one |
| Extra wrapping | {data: {data: {...}}} | Double-wrapped API response |
| Stale data | Correct shape, wrong values | Caching, race condition, stale closure |
Step 4: Trace the Root Cause Upstream
Once a mismatch is found, trace it backward:
- Where does the bad value originate? (API response, user input, database, computation)
- Where does the corruption happen? (transformation, middleware, serialization)
- Why wasn't it caught? (missing validation, no type checking, no tests)
UPSTREAM TRACE
━━━━━━━━━━━━━━
Bad value: userData.email = null
← createUser(userData) [UserService.js:12]
← handleSubmit(formData) [SignupForm.jsx:45]
← formData = getFormValues() [SignupForm.jsx:38]
← email input has name="e-mail" [SignupForm.jsx:15] ← ROOT CAUSE
Mismatch: Form field named "e-mail", code reads "email"
Step 5: Recommend Defensive Measures
After identifying the input issue, suggest:
- Immediate fix: Correct the data at its source
- Validation guard: Add input validation at the function boundary
- Type safety: Add TypeScript types, PropTypes, or schema validation (e.g., Zod, Joi)
- Fail-fast: Throw descriptive errors for invalid inputs instead of silently proceeding
// Example: Adding input validation
function createUser(userData, options = {}) {
// Validate inputs first
if (!userData || typeof userData !== 'object') {
throw new Error('createUser: userData must be an object');
}
if (!userData.name || typeof userData.name !== 'string') {
throw new Error('createUser: userData.name is required and must be a string');
}
if (!userData.email || typeof userData.email !== 'string') {
throw new Error('createUser: userData.email is required and must be a string');
}
// ... proceed with valid inputs
}
Examples
Example 1: API Response Shape Change
User says: "My user list page crashes after the API update."
Expected input: { users: [{id, name, email}] }
Actual input: { data: { users: [{id, name, email}] } }
Root cause: API v2 wraps response in a data envelope. Code reads response.users but should read response.data.users.
Example 2: Type Coercion Bug
User says: "The discount calculation is wrong."
Expected input: discount: 0.15 (number)
Actual input: discount: "15" (string from URL query param)
Root cause: price * discount computes 100 * "15" = 1500 instead of 100 * 0.15 = 15. Query params are always strings.
Troubleshooting
Error: Cannot determine expected input shape
Cause: No types, no docs, no validation—the function accepts anything. Solution: Examine the function body to see which properties/methods it accesses on each parameter. Build the implied contract from usage. Flag the missing contract as a code quality issue.
Error: Input looks correct but function still fails
Cause: The bug is in internal logic, not inputs.
Solution: Confirm inputs are genuinely correct (log them at function entry). If inputs are validated and correct, switch to the trace-execution skill to analyze internal logic.
Error: Input varies across different callers
Cause: Multiple callers pass different shapes. Solution: Check each call site. Identify which caller passes the malformed data. Consider normalizing inputs at the function boundary to accept multiple formats gracefully.