SuperAPI AI Directory

validate-inputs-first

Testing & quality

View repository

Updated 10-15 22:19

About this skill

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"

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:

1. **Trace upstream**: Follow the data from its origin to the function call.
2. **Check transformations**: Identify any mapping, filtering, or formatting applied before the call.
3. **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? (`string` vs `number` vs `object`)
- [ ] **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:

1. **Where does the bad value originate?** (API response, user input, database, computation)
2. **Where does the corruption happen?** (transformation, middleware, serialization)
3. **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:

1. **Immediate fix**: Correct the data at its source
2. **Validation guard**: Add input validation at the function boundary
3. **Type safety**: Add TypeScript types, PropTypes, or schema validation (e.g., Zod, Joi)
4. **Fail-fast**: Throw descriptive errors for invalid inputs instead of silently proceeding

```javascript
// 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.