Skip to content

Diagnostics

Published  · Updated by Claude

Every check the language server runs reports its problems under a stable code, which is the name of its rule: Html/ImgAlt, Syntax/Ruby. In VS Code the code appears next to the message in the Problems panel and in the hover over the underlined text, with the language as the source (erb, haml, slim, or ruby for Jbuilder and Builder views). The ruby-templates check command prints the same messages as file:line:column: severity: message, without the code, and exits with 1 when it finds an error or a warning, or a problem in a .ruby-templates.toml.

The messages are those of Erubi, Rails, Haml 7, Slim 5 and Prism, or quote the section of the HTML standard that is broken. Each entry below shows a template that triggers the code and the message ruby-templates check prints for it, at the same line and column.

Turn a check off in the .ruby-templates.toml of the project, by the rule’s name (the code):

[Html.ImgAlt]
enabled = false


[Html.DuplicateId]
exclude = ["app/views/legacy/**"]


[haml]
disabled_diagnostics = ["Rails/MissingPartial"]

The file is read by the editor and by ruby-templates check, and it beats the settings below. The rules reference describes the file.

Or list the codes to turn off for a language in your settings.json:

"rubyTemplates.erb.disabledDiagnostics": ["Html/ImgAlt", "Html/DuplicateId"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/MissingPartial"]

Before 0.11 the codes were kebab-case words (img-missing-alt, unresolved-render). They still work in both places, and each entry below that had one names its earlier code.

rubyTemplates.slim.disabledDiagnostics also applies to Skim templates, and rubyTemplates.jbuilder.disabledDiagnostics to Jbuilder and Builder views. To silence a whole language, set rubyTemplates.erb.diagnostics.enabled, rubyTemplates.haml.diagnostics.enabled or rubyTemplates.slim.diagnostics.enabled to false (in a .ruby-templates.toml: diagnostics.enabled = false under [erb], [haml] or [slim]). Liquid templates and Thor templates (*.tt) get no diagnostics.

The rules reference lists the checks and formatter rules planned next, next to the ones that report these codes.

ERB

Syntax/UnclosedErbTag

A <% with no %> after it.

Languages ERB

When it fires Erubi only compiles a tag it can close, so a <% without %> stays in the output as text, and the Ruby is printed instead of run. In HTML, < followed by % starts no tag (HTML 13.2.5.6). Reported only where the HTML tokenizer is between tags, not inside attribute values, comments or <script> and <style> bodies, and only in ERB with an HTML host (.html.erb and the like).

Example
app/views/users/show.html.erb
<p><%= @user.name %></p>
<p><%= @user.email</p>
  1. invalid-first-character-of-tag-name: <% without %> is text, and < followed by % starts no tag (HTML 13.2.5.6)2:4Syntax/UnclosedErbTag
Turn it off
[Syntax.UnclosedErbTag]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Syntax/UnclosedErbTag"]

Earlier code unclosed-tag, still accepted.

Html/UnexpectedCloseTag

An end tag with no open element of that name.

Languages ERB

When it fires The end tag closes nothing, so the browser drops it. Also reported for the end tag of a void element ("End tag </br> for a void element") and for a </p> after a block element such as <div> already closed the paragraph. A stray end tag in a partial is not reported, since a partial may close what another one opened. ERB with an HTML host only; checking stops after 20 tag problems in one file.

Example
app/views/users/show.html.erb
<div class="profile">
  <p><%= @user.name %></p>
</div>
</div>
  1. Unexpected end tag </div>: no open element of that name (HTML 13.2.6.4.7)4:1Html/UnexpectedCloseTag
Turn it off
[Html.UnexpectedCloseTag]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/UnexpectedCloseTag"]

Earlier code unexpected-close-tag, still accepted.

Html/MismatchedCloseTag

An end tag that doesn't close the innermost open element.

Languages ERB

When it fires The message names the element that is still open and its line. When the end tag crosses an open element (<div><span></div>), the browser could recover in more than one way, so checking stops there instead of guessing. Reported in partials too. ERB with an HTML host only.

Example
app/views/users/show.html.erb
<div class="profile">
  <span><%= @user.name %></div>
  1. End tag </div> doesn't close the open <span> (line 2) (HTML 13.2.6.4.7)2:26Html/MismatchedCloseTag
Turn it off
[Html.MismatchedCloseTag]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/MismatchedCloseTag"]

Earlier code mismatched-close-tag, still accepted.

Html/UnclosedElement

An element whose end tag never comes.

Languages ERB

When it fires Reported at the start tag when the file ends with the element still open. Elements whose end tag HTML lets you omit (li, p, td, option, …) and void elements are never reported. Partials are exempt, because a _header partial may open a <div> that _footer closes, and so is a file that ends inside an ERB block. ERB with an HTML host only.

Example
app/views/users/index.html.erb
<section class="users">
  <h2>Users</h2>
  1. <section> is never closed (HTML 13.2.6.4.7)1:1Html/UnclosedElement
Turn it off
[Html.UnclosedElement]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/UnclosedElement"]

Earlier code unclosed-element, still accepted.

Rails/OutputControlFlow

Control flow in an output tag.

Languages ERB

When it fires <%= if @user %> is a syntax error, or prints a loop's value (<%= for x in y do %>): an output tag holds an expression, so a tag that opens if, unless, while, until, case, for or begin without closing it, or that starts with else, elsif, when, end, rescue or ensure, needs <% instead. Reported on the =, with a quick fix that removes it; the Ruby syntax errors it would cause are not shown, the template's other errors are. A complete expression such as <%= if a then b else c end %> is not reported.

Example
app/views/users/show.html.erb
<%= if @user %>
  <p>hi</p>
<% end %>
  1. if in an output tag <%=: use <% if … %>1:3Rails/OutputControlFlow
Turn it off
[Rails.OutputControlFlow]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/OutputControlFlow"]

Style/ErbTrimMode

Both %> and -%> in one template.

Languages ERB

When it fires Every closer of the kind the template uses less is reported, -%> when there are as many of each. Comments (<%#) and literal <%% tags are not counted, nor are closers with more on their line. Off by default: turn it on with enabled = true under [Style.ErbTrimMode] in .ruby-templates.toml.

Example
app/views/users/index.html.erb
<% @users.each do |user| -%>
  <li><%= user.name %></li>
<% end %>
  1. -%> here, but most closers in this template are %>1:26Style/ErbTrimMode
Turn it off
[Style.ErbTrimMode]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Style/ErbTrimMode"]

ERB, Haml, and Slim

Html/ImgAlt

An image without alt text.

Languages ERB, Haml, and Slim

When it fires Fires for an <img> (%img in Haml, img in Slim) with no alt attribute and for an image_tag call whose options have no alt:; that one reads "image_tag without alt:". In ERB the call must be in an output tag. Not reported for an <img> with ERB where attributes go (<img <%= attrs %>>), a Haml **attrs or a Slim *attrs, an image_tag given a **splat, or generator-unable-to-provide-required-alt, since each may supply the text. An empty alt="" counts as alt text. Templates that output HTML only: not .js.erb, .text.erb or .xml.haml.

Example
app/views/users/show.html.erb
<img src="<%= @user.avatar_url %>">
  1. <img> without alt: the alt attribute must be specified (HTML 4.8.4.4.1)1:1Html/ImgAlt
Turn it off
[Html.ImgAlt]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/ImgAlt"],
"rubyTemplates.haml.disabledDiagnostics": ["Html/ImgAlt"],
"rubyTemplates.slim.disabledDiagnostics": ["Html/ImgAlt"]

Earlier code img-missing-alt, still accepted.

Html/DuplicateId

The same static id twice in one template.

Languages ERB, Haml, and Slim

When it fires Reported at the second occurrence, naming the line of the first. Only literal values count: an id holding ERB or interpolation is left alone, and so is an element with both #id and id: in Haml (it gets one joined id). Two ids in different branches of a conditional don't clash, and content inside <template> isn't in the document yet. In Haml and Slim, ids are also left alone when one of them sits in a conditional, a loop or a block that the other is not in. Templates that output HTML only.

Example
app/views/users/show.html.erb
<div id="profile">
  <%= @user.name %>
</div>
<div id="profile"></div>
  1. Duplicate id profile (first on line 1): the value must be unique amongst all the IDs in the element's tree (HTML 3.2.6)4:10Html/DuplicateId
Turn it off
[Html.DuplicateId]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/DuplicateId"],
"rubyTemplates.haml.disabledDiagnostics": ["Html/DuplicateId"],
"rubyTemplates.slim.disabledDiagnostics": ["Html/DuplicateId"]

Earlier code duplicate-id, still accepted.

Html/DuplicateAttribute

An attribute given twice on one tag.

Languages ERB, Haml, and Slim

When it fires Reported at the second one, for names written out in ERB start tags, Haml tags (the (…) list and the {…} hash) and Slim tags, and in the literal options of Rails tag helpers. In ERB markup the page gets both, so the second is reported; in Haml and the helpers a repeated key renders once, so the value that is dropped is reported. class and id are left alone in Haml, Slim and the helpers, which merge them. An ERB start tag with ERB inside is left alone. Slim rejects a repeated attribute itself, which is reported as Syntax/Template. Templates that output HTML only.

Example
app/views/users/show.html.erb
<div class="card" class="wide">
  <%= @user.name %>
</div>
  1. Duplicate attribute class on <div>: a start tag must not repeat an attribute (HTML 13.1.2.3)1:19Html/DuplicateAttribute
Turn it off
[Html.DuplicateAttribute]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/DuplicateAttribute"],
"rubyTemplates.haml.disabledDiagnostics": ["Html/DuplicateAttribute"],
"rubyTemplates.slim.disabledDiagnostics": ["Html/DuplicateAttribute"]

Html/AnchorHref

An <a> without an href, which is not a link and cannot be reached with the keyboard.

Languages ERB, Haml, and Slim

When it fires Reported on the tag name. link_to always has an href and is not checked. An <a> with a name, a tabindex, an id and no text of its own, or an attribute ending in :href (:href, x-bind:href), and one inside an <svg>, is left alone, as is one whose attributes may come from code that can't be read (ERB inside the start tag, **attrs, *attrs), is left alone. Templates that output HTML only.

Example
app/views/users/show.html.erb
<a class="btn" data-action="click->menu#open">Menu</a>
  1. <a> without href is not a link and the keyboard can't reach it; use a <button> for an action1:2Html/AnchorHref
Turn it off
[Html.AnchorHref]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/AnchorHref"],
"rubyTemplates.haml.disabledDiagnostics": ["Html/AnchorHref"],
"rubyTemplates.slim.disabledDiagnostics": ["Html/AnchorHref"]

Html/NestedInteractive

An interactive element inside another: a link in a link, a form in a form.

Languages ERB, Haml, and Slim

When it fires Reported on the inner element's tag name or helper name. A link or button (also link_to, tag.a, button_tag with a block) can't hold another link, a button, an <input> that isn't hidden, a <select>, <textarea>, <label>, <iframe>, <details>, controls of <audio> and <video>, or an <img usemap> (HTML 4.5.1); a form can't hold a form, and button_to renders one (HTML 4.10.3). Only nesting the template writes is reported: partials rendered inside the element, a yield, elements started and ended in different branches of an if, and anything in an <svg> or <template> are left alone, and so are a <label> around its <input> and a hidden input. Templates that output HTML only.

Example
app/views/users/show.html.erb
<%= link_to user_path(user) do %>
  <%= link_to "Edit", edit_user_path(user) %>
<% end %>
  1. <a> inside <a>: interactive content can't be nested (HTML 4.5.1)2:7Html/NestedInteractive
Turn it off
[Html.NestedInteractive]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Html/NestedInteractive"],
"rubyTemplates.haml.disabledDiagnostics": ["Html/NestedInteractive"],
"rubyTemplates.slim.disabledDiagnostics": ["Html/NestedInteractive"]

A11y/IframeTitle

An <iframe> without a title, which screen readers use to name it.

Languages ERB, Haml, and Slim

When it fires Reported on the tag name, also for tag.iframe and content_tag :iframe. An aria-label or aria-labelledby (also in an aria: { label: … } hash) names the frame as well. Not reported where attributes may come from code that can't be read. Templates that output HTML only.

Example
app/views/videos/show.html.erb
<iframe src="https://www.youtube.com/embed/<%= @video.youtube_id %>"></iframe>
  1. <iframe> without title: screen readers announce it to name the frame (WCAG 4.1.2)1:2A11y/IframeTitle
Turn it off
[A11y.IframeTitle]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["A11y/IframeTitle"],
"rubyTemplates.haml.disabledDiagnostics": ["A11y/IframeTitle"],
"rubyTemplates.slim.disabledDiagnostics": ["A11y/IframeTitle"]

A11y/GenericLinkText

Link text that says nothing out of context: "click here", "here", "read more".

Languages ERB, Haml, and Slim

When it fires Reported on the text, for an <a> that holds only text and for a link_to, tag.a or content_tag :a given a string literal. The texts are "click here", "here", "read more", "learn more", "more", "more info", "more information", "link", "this link" and "click", in any case and ignoring punctuation. A link with an aria-label, aria-labelledby or aria-describedby, or with ERB, interpolation or other elements inside, is left alone. Templates that output HTML only.

Example
app/views/pricing/show.html.erb
<p>Our new pricing is live. <%= link_to "Click here", pricing_path %></p>
  1. Link text Click here says nothing out of context; name where the link goes (WCAG 2.4.9, F84)1:42A11y/GenericLinkText
Turn it off
[A11y.GenericLinkText]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["A11y/GenericLinkText"],
"rubyTemplates.haml.disabledDiagnostics": ["A11y/GenericLinkText"],
"rubyTemplates.slim.disabledDiagnostics": ["A11y/GenericLinkText"]

Rails/SilentRender

A render in a tag that outputs nothing.

Languages ERB, Haml, and Slim

When it fires A <% %> tag in ERB, and a - line in Haml and Slim, run Ruby and print nothing, so the partial render returns is thrown away. Reported for a render that is a whole statement, also in its block form. Not reported when the result is assigned or passed on. Rails projects only, in templates under app/views. The quick fix changes the tag to <%= (= in Haml and Slim) when the call is alone in its tag or line.

Example
app/views/users/index.html.erb
<% render "users/card", user: @user %>
  1. render in <% %> outputs nothing; use <%=1:4Rails/SilentRender
Turn it off
[Rails.SilentRender]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/SilentRender"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/SilentRender"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/SilentRender"]

Rails/SilentHelper

A helper that returns markup, called in a tag that outputs nothing.

Languages ERB, Haml, and Slim

When it fires link_to, image_tag, content_tag, form_with and the other helpers on the list return markup and do nothing else, so calling one in a <% %> tag (a - line in Haml and Slim) has no effect. Helpers with side effects (content_for, provide), your own helpers and calls on a receiver are not reported. Rails projects only, in templates under app/views. The quick fix changes the tag to <%= (= in Haml and Slim) when the call is alone in its tag or line.

Example
app/views/users/index.html.erb
<% link_to "Home", root_path %>
  1. link_to returns markup that <% %> throws away; use <%=1:4Rails/SilentHelper
Turn it off
[Rails.SilentHelper]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/SilentHelper"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/SilentHelper"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/SilentHelper"]

Rails/DebugOutput

Debugging left in a view.

Languages ERB, Haml, and Slim

When it fires p, pp, puts and debug with arguments, byebug, debugger, binding.irb and binding.pry, as a statement or as the whole of an output tag. A local variable named p, a method on a receiver, a call used as an argument and a call under an if or unless that tests Rails.env are not reported. Rails projects only, in templates under app/views.

Example
app/views/users/index.html.erb
<%= debug @user %>
<p><%= @user.name %></p>
  1. debug is debugging left in the view1:5Rails/DebugOutput
Turn it off
[Rails.DebugOutput]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/DebugOutput"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/DebugOutput"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/DebugOutput"]

Rails/InstanceVariableInPartial

An instance variable read inside a partial.

Languages ERB, Haml, and Slim

When it fires A partial that reads @user works only for the controllers that set it. Pass it as a local instead (render "users/card", user: @user). Reported once per variable and file. Not reported when the partial assigns the variable itself, for defined?(@user), for ActionView's own variables, and in partials under app/views/layouts. Rails projects only, in templates under app/views.

Example
app/views/users/_card.html.erb
<p><%= @user.name %></p>
  1. @user is read in a partial, so it works only where the controller sets it; pass it as a local1:8Rails/InstanceVariableInPartial
Turn it off
[Rails.InstanceVariableInPartial]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/InstanceVariableInPartial"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/InstanceVariableInPartial"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/InstanceVariableInPartial"]

Rails/UnusedStrictLocal

A local in the locals: comment that the partial never uses.

Languages ERB, Haml, and Slim

When it fires Reads, assignments, local_assigns[:name], local_assigns.fetch(:name), binding.local_variable_get(:name), defined?(name) and the default of another local count as use. Nothing is reported when the template passes local_assigns on as a whole or calls binding, local_variables or eval. The quick fix removes the local from the comment. Rails 7.1 or newer, in Rails projects, in templates under app/views.

Example
app/views/users/_card.html.erb
<%# locals: (user:, compact: false) %>
<p><%= user.name %></p>
  1. compact is declared in locals: but the partial never uses it1:21Rails/UnusedStrictLocal
Turn it off
[Rails.UnusedStrictLocal]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/UnusedStrictLocal"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/UnusedStrictLocal"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/UnusedStrictLocal"]

Rails/UnusedBlockArgument

A block argument the block never uses.

Languages ERB, Haml, and Slim

When it fires Looks at plain and optional block parameters, and at the whole block, which spans every tag between do |…| and end. Names starting with _, it and numbered parameters, destructured, *rest, keyword and &block parameters, and blocks that call binding, local_variables or eval are not reported. The quick fix prefixes the name with _. Rails projects only, in templates under app/views.

Example
app/views/users/index.html.erb
<% @users.each do |user, index| %>
  <li><%= user.name %></li>
<% end %>
  1. Block argument index is never used; rename it _index1:26Rails/UnusedBlockArgument
Turn it off
[Rails.UnusedBlockArgument]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/UnusedBlockArgument"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/UnusedBlockArgument"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/UnusedBlockArgument"]

Rails/RenderInLoop

A partial rendered once per item inside a loop.

Languages ERB, Haml, and Slim

When it fires Reported for collection.each do |item| and each_with_index whose whole body is render "partial", name: item, with a string literal for the partial. render partial: "partial", collection: collection, as: :name renders the same markup in one call. Loops that do anything else, other locals and other iterators are not reported. Rails projects only, in templates under app/views.

Example
app/views/users/index.html.erb
<% @users.each do |user| %>
  <%= render "users/card", user: user %>
<% end %>
  1. Partial "users/card" is rendered once per item of a loop; use render partial: "users/card", collection: @users, as: :user2:7Rails/RenderInLoop
Turn it off
[Rails.RenderInLoop]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/RenderInLoop"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/RenderInLoop"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/RenderInLoop"]

Rails/UnsafeOutput

Output that skips Rails' HTML escaping.

Languages ERB, Haml, and Slim

When it fires raw x, x.html_safe and safe_concat x are reported on the method name, and the unescaped output markers <%== (ERB), != and !~ (Haml) and == (Slim) on the marker. raw x.to_json in a <script> is the usual idiom and is not reported. Not reported when the value is certainly safe: a literal without interpolation, yield, content_for, a t call with a literal key ending in _html or .html, or a call to sanitize, safe_join, strip_tags, json_escape, j, escape_javascript, h, html_escape, link_to, content_tag, render, capture and the like, or an escaper on ERB::Util, CGI or Rack::Utils. Not checked in text templates. Rails projects only, in templates under app/views.

Example
app/views/users/show.html.erb
<%= raw @user.bio %>
  1. raw outputs @user.bio without escaping; use sanitize if it holds user HTML1:5Rails/UnsafeOutput
Turn it off
[Rails.UnsafeOutput]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/UnsafeOutput"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/UnsafeOutput"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/UnsafeOutput"]

Rails/I18nMissingKey

A lazy translation key with no entry in config/locales.

Languages ERB, Haml, and Slim

When it fires A literal lazy key (t(".title"), translate, with or without parentheses) is turned into its full key as Rails does (users/_form gives users.form.title, a layout keeps its folder) and must be in the default locale of the YAML files under config/locales: config.i18n.default_locale, en without one. The closest key of the view within two edits is offered. Absolute keys are not checked, since gems supply keys such as date.formats and helpers.submit.create; nor is a key with default:, scope: or locale:, a key with interpolation, or one in a view named for a gem in the Gemfile.lock. Nothing is reported without a config/application.rb or a locale file for the default locale, or when translations may come from elsewhere: Ruby locale files, I18n.load_path, I18n.backend, a gem that keeps them in a store. With config.i18n.fallbacks a key in any locale counts. Rails projects only, in templates under app/views.

Example
app/views/users/index.html.erb
<h1><%= t(".titel") %></h1>
  1. Translation users.index.titel is missing in the en locale; did you mean .title?1:11Rails/I18nMissingKey
Turn it off
[Rails.I18nMissingKey]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/I18nMissingKey"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/I18nMissingKey"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/I18nMissingKey"]

Rails/UnknownRoute

A *_path or *_url helper that the app's routes don't define.

Languages ERB, Haml, and Slim

When it fires A *_path or *_url call without a receiver that no route of the app defines but that is within two edits of one (usr_path for user_path) is reported, with that route offered; a name with no route near it may come from a gem, a concern or lib, which can't be seen, so it is left alone. The routes are read with bin/rails runner once the editor has loaded them (setting rubyTemplates.routes.enabled), so this is reported in the editor and not by ruby-templates check. Not reported: a call on a receiver (main_app.users_path, an engine's admin.users_path), Rails' asset and polymorphic helpers, rails_*, turbo_*, action_mailbox_*, omniauth_*, vite_* and asset_pack_* helpers, Devise's own helpers (session_path, edit_password_url, …) when the Gemfile.lock has devise, a view in a folder named for a gem (spree/, rails_admin/), a helper the app defines in app/helpers or names with a symbol (helper_method, delegate, define_method), a call under defined?, a route declared with direct, and a local variable or block parameter. A bare name (item_path with resources :items) could be a local the caller passes, so it is reported only when called with arguments or when the template's strict locals don't list it. Rails projects only, in templates under app/views.

Example
app/views/users/index.html.erb
<%= link_to @user.name, usr_path(@user) %>
  1. usr_path is not a route helper of this app; did you mean user_path?1:25Rails/UnknownRoute
Turn it off
[Rails.UnknownRoute]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/UnknownRoute"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/UnknownRoute"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/UnknownRoute"]

Turbo/PermanentRequiresId

An element with data-turbo-permanent and no id.

Languages ERB, Haml, and Slim

When it fires Turbo keeps a permanent element across visits by matching its id on the next page, so without one the attribute does nothing. Read from ERB start tags, Haml and Slim tags, and the options of tag.div, content_tag, link_to and the form and button helpers; any value counts. An id of any kind (#id in Haml and Slim, an object reference such as %div[@user]) satisfies it. Nothing is reported where attributes may come from code that can't be read: ERB inside the start tag, a Haml **attrs or data: attrs, a Slim *attrs or data= hash, a helper's **options, or a value that isn't a literal. Rails projects only, in templates under app/views.

Example
app/views/layouts/application.html.erb
<div data-turbo-permanent>
  <%= render 'shared/player' %>
</div>
  1. data-turbo-permanent needs an id: Turbo matches the element across pages by it1:6Turbo/PermanentRequiresId
Turn it off
[Turbo.PermanentRequiresId]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Turbo/PermanentRequiresId"],
"rubyTemplates.haml.disabledDiagnostics": ["Turbo/PermanentRequiresId"],
"rubyTemplates.slim.disabledDiagnostics": ["Turbo/PermanentRequiresId"]

Stimulus/UnknownController

A data-controller name that no Stimulus controller has.

Languages ERB, Haml, and Slim

When it fires Each name in a static data-controller (also data: { controller: "…" } in Haml and in the options of tag, content_tag, link_to, button_to and the form helpers) must be a controller file in app/javascript/controllers (search_controller.js, admin/users_controller.js for admin--users; -controller, .ts, .jsx, .tsx and .mjs too) or a name a script in app/javascript registers with application.register("name", …). The closest controller within two edits is offered. Nothing is reported when the app has no app/javascript/controllers folder, loads controllers from another folder (eagerLoadControllersFrom("components", …), a context or glob outside the controllers folder, an importmap pin), registers one under a name the code computes, and not for a name with ERB or interpolation. Rails projects only, in templates under app/views.

Example
app/views/users/index.html.erb
<div data-controller="serch">
  <%= text_field_tag :q %>
</div>
  1. No Stimulus controller serch in app/javascript/controllers; did you mean search?1:23Stimulus/UnknownController
Turn it off
[Stimulus.UnknownController]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Stimulus/UnknownController"],
"rubyTemplates.haml.disabledDiagnostics": ["Stimulus/UnknownController"],
"rubyTemplates.slim.disabledDiagnostics": ["Stimulus/UnknownController"]

Stimulus/UnknownTargetOrAction

A data-*-target or data-action method that its Stimulus controller doesn't have.

Languages ERB, Haml, and Slim

When it fires Static data-<controller>-target names are checked against the controller's static targets = ["…"], and the method of each data-action descriptor against the members its class defines (also data: { search_target: "…", action: "…" } in Haml and in the options of the Rails helpers). The closest name within two edits is offered. Only a controller that is one file in app/javascript/controllers and can be read with confidence is checked: a class that extends something other than Controller (a base controller is followed when imported by a relative path), computed member names, decorators, mixins and a static targets that is not a plain list skip it. Rails projects only, in templates under app/views.

Example
app/views/users/index.html.erb
<div data-controller="search">
  <input data-search-target="querry">
</div>
  1. search controller has no target querry; did you mean query?2:30Stimulus/UnknownTargetOrAction
Turn it off
[Stimulus.UnknownTargetOrAction]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Stimulus/UnknownTargetOrAction"],
"rubyTemplates.haml.disabledDiagnostics": ["Stimulus/UnknownTargetOrAction"],
"rubyTemplates.slim.disabledDiagnostics": ["Stimulus/UnknownTargetOrAction"]

Layout/LineLength

A line longer than the limit.

Languages ERB, Haml, and Slim

When it fires Every line over max characters (100 unless set) is reported from the first character past the limit. Characters are counted, a tab is one, trailing whitespace is not counted, and a line that cannot be made shorter is skipped: one with no space after its indentation, such as a long URL, and the # locals: comment of strict locals. Off by default: turn it on with enabled = true under [Layout.LineLength] in .ruby-templates.toml, where max sets the limit.

Example
app/views/announcements/show.html.erb
<p class="lead text-muted"><%= link_to "Read the full announcement", announcement_path(@announcement), class: "link" %></p>
  1. line is 123 characters long; the limit is 1001:101Layout/LineLength
Turn it off
[Layout.LineLength]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Layout/LineLength"],
"rubyTemplates.haml.disabledDiagnostics": ["Layout/LineLength"],
"rubyTemplates.slim.disabledDiagnostics": ["Layout/LineLength"]

Haml

Syntax/InconsistentIndent

Indentation Haml can't follow.

Languages Haml

When it fires Haml's own indentation errors: a width other than the one the rest of the document uses, tabs mixed with spaces ("Indentation can't use both tabs and spaces."), or a line more than one level deeper than the one before ("The line was indented 2 levels deeper than the previous line."). Haml stops at its first error, so only that one is shown, and the Ruby isn't checked until it is fixed.

Example
app/views/users/index.html.haml
%section
  %h2 Users
  %ul
   %li= @user.name
  1. Inconsistent indentation: 3 spaces used for indentation, but the rest of the document was indented using 2 spaces.4:1Syntax/InconsistentIndent
Turn it off
[Syntax.InconsistentIndent]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/InconsistentIndent"]

Earlier code inconsistent-indent, still accepted.

Syntax/TopLevelIndent

The first line is indented.

Languages Haml

When it fires Haml measures every level from the first line, which must start at column 1.

Example
app/views/users/show.html.haml
  %p= @user.name
  1. Indenting at the beginning of the document is illegal.1:1Syntax/TopLevelIndent
Turn it off
[Syntax.TopLevelIndent]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/TopLevelIndent"]

Earlier code top-level-indent, still accepted.

Syntax/IllegalNesting

Lines nested under something that can't hold them.

Languages Haml

When it fires Haml rejects lines nested under a tag that already has content on its own line, under plain text, and under a self-closing tag such as %br/; each has its own "Illegal nesting" message. Nesting under !!! has its own code, Syntax/DoctypeChildren.

Example
app/views/users/show.html.haml
%p= @user.name
  %span Admin
  1. Illegal nesting: content can't be both given on the same line as %p and nested within it.2:3Syntax/IllegalNesting
Turn it off
[Syntax.IllegalNesting]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/IllegalNesting"]

Earlier code illegal-nesting, still accepted.

Syntax/UnmatchedBranch

An else, elsif or when that doesn't line up with its block.

Languages Haml

When it fires In Haml a branch keyword must stand at the indentation of the - if or - case it continues. One at another level is reported with the expected and actual levels; one with no open block before it reads "Got "else" with no preceding "if"". The first when may sit one level deeper than its case.

Example
app/views/users/index.html.haml
- if @users.any?
  = render @users
  - else
    %p No users
  1. "else" is indented at wrong level: expected 0, but was at 1.3:3Syntax/UnmatchedBranch
Turn it off
[Syntax.UnmatchedBranch]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/UnmatchedBranch"]

Earlier code unmatched-branch, still accepted.

Syntax/IllegalElement

A . or # with no class or id name after it.

Languages Haml

When it fires Haml needs a name after each . and # in a tag (%p.lead, #main).

Example
app/views/users/show.html.haml
%p. Hello
  1. Illegal element: classes and ids must have values.1:1Syntax/IllegalElement
Turn it off
[Syntax.IllegalElement]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/IllegalElement"]

Earlier code illegal-element, still accepted.

Syntax/UnknownFilter

A filter Haml doesn't have.

Languages Haml

When it fires The name after : isn't one of Haml's filters (:javascript, :css, :markdown, :plain, :ruby, …), usually a typo. Haml raises this when it compiles the template. A name that isn't a word at all is a Syntax/Template ("Invalid filter name").

Example
app/views/users/show.html.haml
:mardown
  # Users
  1. FilterCompiler for 'mardown' was not found1:1Syntax/UnknownFilter
Turn it off
[Syntax.UnknownFilter]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/UnknownFilter"]

Earlier code unknown-filter, still accepted.

Syntax/SelfClosingContent

Content after a tag closed with /.

Languages Haml

When it fires A tag ending in / renders as <br /> and can hold nothing on its own line.

Example
app/views/users/show.html.haml
%br/ Hello
  1. Self-closing tags can't have content.1:1Syntax/SelfClosingContent
Turn it off
[Syntax.SelfClosingContent]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/SelfClosingContent"]

Earlier code self-closing-content, still accepted.

Haml and Slim

Syntax/DoctypeChildren

Lines nested under the doctype.

Languages Haml and Slim

When it fires The doctype line (!!! in Haml, doctype in Slim) takes no children; the %html or html that follows belongs at the same level. Slim reports it as "Unexpected indentation".

Example
app/views/layouts/application.html.haml
!!! 5
  %html
  1. Illegal nesting: nesting within a header command is illegal.2:3Syntax/DoctypeChildren
Turn it off
[Syntax.DoctypeChildren]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/DoctypeChildren"],
"rubyTemplates.slim.disabledDiagnostics": ["Syntax/DoctypeChildren"]

Earlier code doctype-children, still accepted.

Syntax/ExplicitEnd

A - end line.

Languages Haml and Slim

When it fires Haml and Slim close a Ruby block by un-indenting, and both refuse an explicit - end. Slim's message is shown here; Haml's starts with "You don't need to use "- end" in Haml." and goes on with an example. Skim templates, whose code is CoffeeScript, are not checked for it.

Example
app/views/users/show.html.slim
- if @user.admin?
  p Admin
- end
  1. Explicit end statements are forbidden3:1Syntax/ExplicitEnd
Turn it off
[Syntax.ExplicitEnd]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/ExplicitEnd"],
"rubyTemplates.slim.disabledDiagnostics": ["Syntax/ExplicitEnd"]

Earlier code explicit-end, still accepted.

Syntax/UnclosedAttribute

An attribute list or value that is never closed.

Languages Haml and Slim

When it fires Slim reports the bracket it expected ("Expected closing delimiter )", or ] or }), or "Unexpected end of file" for a quote left open. Haml reports "Unbalanced brackets." for a { or ( attribute list that doesn't close, and "Invalid attribute list" for one it can't read.

Example
app/views/users/show.html.slim
a href=(user_path(@user) Profile
  1. Expected closing delimiter )1:33Syntax/UnclosedAttribute
Turn it off
[Syntax.UnclosedAttribute]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/UnclosedAttribute"],
"rubyTemplates.slim.disabledDiagnostics": ["Syntax/UnclosedAttribute"]

Earlier code unclosed-attribute, still accepted.

Syntax/Template

Any other error the Haml or Slim parser raises.

Languages Haml and Slim

When it fires The parser errors that have no code of their own. In Haml: "Invalid tag", "Invalid filter name" and "There's no Ruby code for = to evaluate." In Slim: "Unknown line indicator", "Illegal shortcut", "Invalid empty attribute", "Expected attribute", "Expected tag" for a block expansion with nothing after it (ul:), "Unexpected text after closed tag", "Multiple id attributes specified" (for any repeated attribute) and "Invalid doctype".

Example
app/views/users/show.html.haml
%p=
  1. There's no Ruby code for = to evaluate.1:1Syntax/Template
Turn it off
[Syntax.Template]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/Template"],
"rubyTemplates.slim.disabledDiagnostics": ["Syntax/Template"]

Earlier code syntax-error, still accepted.

Html/VoidChildren

Content inside a void element such as img or input.

Languages Haml and Slim

When it fires Void elements have no end tag, so the content would end up after them. Haml reports content on the tag's line or nested under it. Slim reports lines nested under a void element, and "Unexpected indentation" under a tag closed with /. In Slim it applies to HTML templates only, not to .xml.slim and other formats.

Example
app/views/users/show.html.slim
img src="/logo.png"
  | Logo
  1. img is a void element and can't have children2:3Html/VoidChildren
Turn it off
[Html.VoidChildren]
enabled = false
Or in settings.json
"rubyTemplates.haml.disabledDiagnostics": ["Html/VoidChildren"],
"rubyTemplates.slim.disabledDiagnostics": ["Html/VoidChildren"]

Earlier code void-children, still accepted.

Slim

Syntax/BadDedent

A line un-indented to a level no open line has.

Languages Slim

When it fires When a line moves left, Slim needs it to land on the indentation of a line still open above it. Here li Two stops between ul and li One.

Example
app/views/users/index.html.slim
section
  ul
    li One
   li Two
  1. Malformed indentation4:4Syntax/BadDedent
Turn it off
[Syntax.BadDedent]
enabled = false
Or in settings.json
"rubyTemplates.slim.disabledDiagnostics": ["Syntax/BadDedent"]

Earlier code bad-dedent, still accepted.

Syntax/UnknownEngine

An embedded engine Slim doesn't know.

Languages Slim

When it fires A lone name: line opens an embedded engine. Slim's own are ruby, javascript, css, sass, scss, coffee, markdown, textile, rdoc and asciidoc; for any other Slim raises "Expected tag". Engines your app registers (such as mjml) go in rubyTemplates.slim.extraEngines.

Example
app/views/users/show.html.slim
markdwn:
  # Users
  1. Expected tag1:9Syntax/UnknownEngine
Turn it off
[Syntax.UnknownEngine]
enabled = false
Or in settings.json
"rubyTemplates.slim.disabledDiagnostics": ["Syntax/UnknownEngine"]

Earlier code unknown-engine, still accepted.

ERB, Haml, Slim, and Jbuilder

Syntax/Ruby

Ruby in the template that doesn't parse.

Languages ERB, Haml, Slim, and Jbuilder

When it fires The template is compiled the way Erubi, Haml or Slim compile it, and the result is parsed with Prism for the project's Ruby version; the message is Prism's. Haml and Slim code is checked only once the template itself parses. Also covers the locals: comment of strict locals (Rails 7.1 and later), for instance a local without a colon: # locals: (user) gives "user set as non-keyword argument for … Locals can only be set as keyword arguments." In Jbuilder and Builder views only that comment is checked, and in Skim templates nothing, since their code is CoffeeScript.

Example
app/views/users/index.html.erb
<ul>
  <% @users.each do |user| %>
    <li><%= user.name %></li>
</ul>
  1. unexpected end-of-input, assuming it is closing the parent top level context4:1Syntax/Ruby
Turn it off
[Syntax.Ruby]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Syntax/Ruby"],
"rubyTemplates.haml.disabledDiagnostics": ["Syntax/Ruby"],
"rubyTemplates.slim.disabledDiagnostics": ["Syntax/Ruby"],
"rubyTemplates.jbuilder.disabledDiagnostics": ["Syntax/Ruby"]

Earlier code ruby-syntax, still accepted.

Rails/MissingPartial

A render of a partial or template that isn't in app/views.

Languages ERB, Haml, Slim, and Jbuilder

When it fires Checks literal names in render "users/card", render partial:, render template: and Jbuilder's json.partial!, in a Rails app (actionview in Gemfile.lock) and in templates under app/views. A name with a folder is checked when that folder exists in app/views; a bare name (render "form") only in an action template, not a partial, whose controller chain is in the app, and the message then lists each folder Rails searches. Folders of view-shipping gems such as devise/ are skipped. The message is Rails' "Missing partial" or "Missing template", without the formats and view paths Rails adds.

Example
app/views/users/show.html.erb
<%= render "users/profile", user: @user %>
  1. Missing partial users/_profile1:13Rails/MissingPartial
Turn it off
[Rails.MissingPartial]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/MissingPartial"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/MissingPartial"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/MissingPartial"],
"rubyTemplates.jbuilder.disabledDiagnostics": ["Rails/MissingPartial"]

Earlier code unresolved-render, still accepted.

Rails/StrictLocals

A render whose locals don't match the partial's locals: comment.

Languages ERB, Haml, Slim, and Jbuilder

When it fires With Rails 7.1 or later in Gemfile.lock, a literal render that resolves to exactly one partial is compared with that partial's strict locals. Here _card.html.erb starts with <%# locals: (user:) -%>. The messages are Rails': "missing local", "missing keywords", "unknown local", "unknown keywords", and "no locals accepted" for a partial declaring locals: (). From Rails 7.2 they end with the partial's path. The layout: of a render isn't checked, since it gets the call's locals.

Example
app/views/users/show.html.erb
<%= render "users/card", person: @user %>
  1. missing local: :user for app/views/users/_card.html.erb1:13Rails/StrictLocals
Turn it off
[Rails.StrictLocals]
enabled = false
Or in settings.json
"rubyTemplates.erb.disabledDiagnostics": ["Rails/StrictLocals"],
"rubyTemplates.haml.disabledDiagnostics": ["Rails/StrictLocals"],
"rubyTemplates.slim.disabledDiagnostics": ["Rails/StrictLocals"],
"rubyTemplates.jbuilder.disabledDiagnostics": ["Rails/StrictLocals"]

Earlier code strict-locals, still accepted.