{"record":{"id":"692d4d9a15543d31","repo":"instructure/canvas-lms","slug":"i18n-localizer-executed-from-context-less-controller","errorCode":null,"errorMessage":"[I18N] localizer executed from context-less controller","messagePattern":"\\[I18N\\] localizer executed from context-less controller","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/controllers/application_controller.rb","lineNumber":1096,"sourceCode":"    request.env[\"extra-request-cost\"] = current_cost + amount\n  end\n\n  def assign_localizer\n    I18n.localizer = lambda do\n      context_hash = {\n        context: @context,\n        user: not_fake_student_user,\n        root_account: @domain_root_account\n      }\n      if request.present?\n        # if for some reason this gets stuck\n        # as global state on I18n (cleanup failure), we don't want it to\n        # explode trying to access a non-existant request.\n        context_hash[:session_locale] = session[:locale]\n        context_hash[:session_timezone] = session[:timezone]\n        context_hash[:accept_language] = request.headers[\"Accept-Language\"]\n      else\n        logger.warn(\"[I18N] localizer executed from context-less controller\")\n      end\n      infer_locale context_hash\n    end\n  end\n\n  def set_locale\n    store_session_locale\n    assign_localizer\n    yield if block_given?\n  ensure\n    # this resets any locale set in set_locale_with_localizer (implicitly called\n    # on any translation call)\n    I18n.locale = I18n.default_locale\n    I18n.localizer = nil\n  end\n\n  def set_timezone\n    store_session_timezone","sourceCodeStart":1078,"sourceCodeEnd":1114,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/application_controller.rb#L1078-L1114","documentation":"Canvas's locale inference (assign_localizer) reads request-scoped data (session locale, timezone, Accept-Language header) to pick a UI locale. When the controller has no valid request/session context (e.g. invoked outside a real HTTP request), it logs this warning and falls back to locale inference with an empty context hash instead of reading session/headers.","triggerScenarios":"A controller (or render/service path) calls the localizer / infer_locale chain when the request/session context is nil — controllers instantiated in console, specs, or non-HTTP contexts where `session` and `request.headers` are unavailable.","commonSituations":"Running controller code in rails console or in specs without a request; custom controllers that bypass ApplicationController's request lifecycle; API-only controllers with no session; render calls outside the request cycle.","solutions":["Ensure the controller is only invoked during a real request/render cycle with a proper request context","In specs, use a controller/request spec so session and request objects exist (or stub session/request)","In console or background jobs, call infer_locale with an explicit context_hash instead of the localizer","If intentional, it is safe to ignore: the code proceeds with an empty context hash and infers a default locale"],"exampleFix":"// before (spec/console)\nMyController.new.send(:assign_localizer)\n// after\ncontroller = MyController.new\nallow(controller).to receive(:session).and_return(locale: 'en')\nallow(controller).to receive(:request).and_return(ActionDispatch::TestRequest.create)","handlingStrategy":"fallback","validationCode":"def request_context_available?\n  request.present? && session.present?\nend","typeGuard":"def has_request_context?(controller)\n  controller.respond_to?(:request) && controller.request.present?\nend","tryCatchPattern":"begin\n  locale = controller.send(:infer_locale, context_hash)\nrescue StandardError\n  I18n.default_locale\nend","preventionTips":["Only invoke localizer logic inside a real request cycle","In specs use controller/request specs with a test request object","Build context_hash explicitly in background/console code instead of reading session/request"],"tags":["i18n","locale","rails","request-context"],"backgroundTag":"missing-config-value","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}