Summary
createAuthMiddleware (and createAuthIdentifier) declare BetterAuthInstance.getSession with a wider headers parameter than Better Auth's actual auth.api.getSession. Because function parameters are contravariant, passing a real auth instance fails type-checking under strict / strictFunctionTypes.
Type declared by evlog
// dist/better-auth/index.d.mts
interface BetterAuthInstance {
api: {
getSession: (opts: {
headers: Headers | Record<string, string | string[] | undefined>;
}) => Promise<{ user: Record<string, unknown>; session: Record<string, unknown> } | null>;
};
}
Type provided by Better Auth
getSession: (context: { headers: Headers; ... }) => Promise<...>
Better Auth only accepts Headers, not the wider union — so its getSession is not assignable to evlog's slot.
Reproduction
import { betterAuth } from 'better-auth';
import { createAuthMiddleware } from 'evlog/better-auth';
const auth = betterAuth({ /* any config */ });
export const identify = createAuthMiddleware(auth);
// ^^^^
// TS2345: Argument of type 'Auth<...>' is not assignable to parameter of type 'BetterAuthInstance'.
// The types of 'api.getSession' are incompatible between these types.
// Type '(<R, H>(context: { headers: Headers; ... }) => ...)' is not assignable to
// '(opts: { headers: Headers | Record<...>; }) => ...'.
// Types of property 'headers' are incompatible.
// Type 'Headers | Record<...>' is not assignable to 'Headers'.
Versions
evlog: 2.16.0
better-auth: 1.6.9 (also reproduces on 1.5.6)
typescript: 5.x with strict: true
Suggested fix
Narrow the interface to just Headers, since that is what every Better Auth release actually accepts:
interface BetterAuthInstance {
api: {
getSession: (opts: { headers: Headers }) => Promise<{ user: Record<string, unknown>; session: Record<string, unknown> } | null>;
};
}
If support for header records is desired internally, evlog can normalize them to Headers before calling auth.api.getSession.
Workaround
Wrap the auth instance with a small adapter that converts the union to Headers before calling through:
function toHeaders(input: Headers | Record<string, string | string[] | undefined>): Headers {
if (input instanceof Headers) return input;
const h = new Headers();
for (const [k, v] of Object.entries(input)) {
if (v === undefined) continue;
Array.isArray(v) ? v.forEach(x => h.append(k, x)) : h.set(k, v);
}
return h;
}
export const identify = createAuthMiddleware({
api: { getSession: ({ headers }) => auth.api.getSession({ headers: toHeaders(headers) }) },
});
Summary
createAuthMiddleware(andcreateAuthIdentifier) declareBetterAuthInstance.getSessionwith a widerheadersparameter than Better Auth's actualauth.api.getSession. Because function parameters are contravariant, passing a realauthinstance fails type-checking understrict/strictFunctionTypes.Type declared by evlog
Type provided by Better Auth
Better Auth only accepts
Headers, not the wider union — so itsgetSessionis not assignable to evlog's slot.Reproduction
Versions
evlog: 2.16.0better-auth: 1.6.9 (also reproduces on 1.5.6)typescript: 5.x withstrict: trueSuggested fix
Narrow the interface to just
Headers, since that is what every Better Auth release actually accepts:If support for header records is desired internally, evlog can normalize them to
Headersbefore callingauth.api.getSession.Workaround
Wrap the auth instance with a small adapter that converts the union to
Headersbefore calling through: