Two HTTP clients create two error-handling conventions, two interceptor pipelines, two retry policies, and two authentication header-injection paths. A token refresh that works in your axios interceptor silently skips every ky call, producing inconsistent 401 behavior that depends on which file made the request. Request/response logging captures only half your traffic, and timeout defaults differ (axios has no default timeout, ky defaults to 10 seconds) creating unpredictable production behavior.
High because inconsistent auth-header injection and timeout behavior produce hard-to-reproduce production incidents.
Wrap your chosen client once and import only that wrapper everywhere.
// src/lib/http.ts
import ky from 'ky'
export const http = ky.extend({
prefixUrl: '/api',
timeout: 10000,
hooks: { beforeRequest: [addAuthHeader] },
})
Rewrite every axios call site to use import { http } from '@/lib/http', then npm uninstall axios. Add an ESLint rule forbidding direct imports of both libraries outside src/lib/http.ts.
ID: ai-slop-code-drift.tooling-stack-drift.dual-http-client
Severity: high
What to look for: Apply the three-condition rule against this exact HTTP client allowlist: axios, ky, got, node-fetch, cross-fetch, isomorphic-fetch, superagent, request (deprecated), phin, undici. Note that fetch (built into Node 18+ and all browsers) is NOT a package — do not count it. Count all REMAINING libraries from the allowlist. EXCEPT: if the project legitimately needs both browser fetch and a Node-side library (e.g., undici for HTTP/2 streaming), check if the Node-side library is isolated to a specific server-only directory like app/api/ or server/ AND is only imported by ≤3 files — if so, treat it as an escape hatch and pass.
Pass criteria: 0 or 1 HTTP client libraries from the allowlist actively used. Report even on pass: "Canonical HTTP client: [name or 'fetch (built-in)'] ([N] importing files)."
Fail criteria: 2 or more HTTP client libraries from the allowlist meet all three conditions (at least 1 non-escape-hatch importing file) and the escape-hatch exception does not apply.
Skip (N/A) when: 0 HTTP client libraries from the allowlist appear in RUNTIME_DEPS.
Detail on fail: "2 active HTTP clients: 'axios' (15 files) AND 'ky' (6 files). Pick one and convert the rest."
Remediation: Two HTTP clients means two error-handling patterns, two interceptor systems, two retry strategies. Pick one and migrate:
// Bad: some files use axios, others use ky
// Good: a single src/lib/http.ts wrapper around your chosen client
// src/lib/http.ts
import ky from 'ky'
export const http = ky.extend({ prefixUrl: '/api' })
// Then everywhere else:
import { http } from '@/lib/http'
After migration, npm uninstall axios.