validate-inputs-first
$npx skills add blunotech-dev/skills --skill validate-inputs-firstEnsure the data entering the function is the expected shape. Use when debugging unexpected behavior, null reference errors, type errors, or data-related crashes. Activate when a user says "validate inputs", "check the data", "what's being passed in", "wrong data type", "unexpected null", or "the function receives wrong arguments".
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
}
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.
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.