{"record":{"id":"72a93ebddd5524a5","repo":"apereo/cas","slug":"no-clientinfo-could-be-found-returning-empty-clie","errorCode":null,"errorMessage":"No ClientInfo could be found. Returning empty ClientInfo object.","messagePattern":"No ClientInfo could be found\\. Returning empty ClientInfo object\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-audit-api/src/main/java/org/apereo/inspektr/common/spi/DefaultClientInfoResolver.java","lineNumber":25,"sourceCode":"import module java.base;\nimport org.aspectj.lang.JoinPoint;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Default implementation that gets it from the {@link ThreadLocal}.\n *\n * @author Scott Battaglia\n * @since 1.0\n */\n@Slf4j\npublic class DefaultClientInfoResolver implements ClientInfoResolver {\n    @Override\n    public ClientInfo resolveFrom(final JoinPoint joinPoint, @Nullable final Object retVal) {\n        val clientInfo = ClientInfoHolder.getClientInfo();\n        if (clientInfo != null) {\n            return resolveClientInfo(clientInfo);\n        }\n        LOGGER.warn(\"No ClientInfo could be found. Returning empty ClientInfo object.\");\n        return ClientInfo.empty();\n    }\n\n    protected ClientInfo resolveClientInfo(final ClientInfo clientInfo) {\n        return clientInfo;\n    }\n}\n","sourceCodeStart":7,"sourceCodeEnd":33,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-audit-api/src/main/java/org/apereo/inspektr/common/spi/DefaultClientInfoResolver.java#L7-L33","documentation":"DefaultClientInfoResolver.resolveFrom reads ClientInfoHolder.getClientInfo(), which is populated by the ClientInfo thread-local filter/interceptor. When no ClientInfo is bound to the current thread (audit executed outside a request handled by that filter), it warns and returns ClientInfo.empty(), so audit records lack client IP/host/user-agent data rather than failing.","triggerScenarios":"Audit aspect (Inspektr) runs on a code path where the ClientInfoHolderFilter or the thread-local population step never ran: background/scheduled jobs, direct service invocations in tests, asynchronous threads spawned from requests, or the client-info filter not registered in the filter chain.","commonSituations":"CAS deployments missing the clientInfoHolder filter configuration; audit of ticket-expiration cleanup jobs and other non-HTTP executors; unit/integration tests calling audited services directly; custom filters reordering so the client-info filter runs after the audited code.","solutions":["Ensure the ClientInfoHolderFilter (Inspektr client-info filter) is registered and ordered before audited endpoints in the CAS filter chain.","For non-HTTP threads, populate ClientInfoHolder manually before invoking audited code (ClientInfoHolder.setHeader/clientInfo) and clear it afterward.","Treat the warning as benign for background jobs where client info is inherently absent.","In tests, set up ClientInfoHolder in the test setup or ignore this warning in test logs."],"exampleFix":"// before: background thread audits with no client info\nexecutor.submit(() -> ticketService.someAuditedOperation());\n\n// after: supply client info for the thread\nexecutor.submit(() -> {\n    ClientInfoHolder.setClientInfo(new ClientInfo(\"127.0.0.1\", \"localhost\"));\n    try {\n        ticketService.someAuditedOperation();\n    } finally {\n        ClientInfoHolder.clearClientInfo();\n    }\n});","handlingStrategy":"try-catch","validationCode":"ClientInfo clientInfo = ClientInfoHolder.getClientInfo();\nif (clientInfo == null) {\n    ClientInfoHolder.setClientInfo(ClientInfo.empty()); // or a synthetic ClientInfo\n}","typeGuard":null,"tryCatchPattern":"try {\n    auditedService.operation();\n} finally {\n    if (ClientInfoHolder.getClientInfo() != null) {\n        ClientInfoHolder.clearClientInfo();\n    }\n}","preventionTips":["Register the Inspektr ClientInfoHolderFilter before audited code paths.","Populate ClientInfoHolder in background threads and tests before invoking audited services.","Clear the thread-local in finally blocks to avoid leakage across pooled threads."],"tags":["audit","client-info","thread-local","inspektr"],"backgroundTag":"missing-env-var","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}