Skip to main content

Understanding undetermined

Every SQLens result is one of three values: pass, fail, or undetermined. The first two are familiar. The third is the one that makes the tool trustworthy, and it is worth a page of its own.

The three-valued model

A check that cannot run is never reported as a pass. If SQLens could not obtain a migration's SQL, could not reach the server, or could not determine the server version, it says so — as an undetermined result with a named reason — instead of staying quiet and letting the run look clean. A green run that never actually checked something is the most expensive kind of false confidence there is, so SQLens does not produce one. A skip without a reason is a bug, not a feature.

An undetermined is not a failure and it is not your mistake. It means SQLens was honest about a limit rather than guessing past it.

The pre-scan is deliberately conservative

Most undetermined results in the lint suite come from the static pre-scan, which reads a migration before it is captured and flags anything pretend mode cannot see truthfully — a side effect it would fire, a query result it depends on, an introspection guard, a call into your own code. The pre-scan is deliberately conservative: it would rather flag a migration you know is fine than let one slip through that quietly does something pretend could not observe. A flag is not an accusation. It is SQLens saying "I cannot be sure about this one in pretend mode", and the reasons below are how you make it sure.

A stated honesty limit. The pre-scan is syntactic pattern recognition, not taint or data-flow analysis. It sees one level: it recognizes a call, but it does not follow the call into the code it reaches. That is most visible in CAP.PRESCAN.INDIRECT_CALL, which flags a migration that reaches its effect through your own code — (new Backfill)->run() — without knowing what run() does. It flags conservatively because the alternative is a silent pass exactly where a real side effect could hide. When you know a class is safe, the allowlist is the intended way to say so.

The three ways to resolve one

  1. Run it in shadow mode. Shadow mode executes the migration for real against a disposable database, so the empty-result and guarded-block limits of pretend mode disappear. This is the truth mode, and it is what most undetermined reasons point you to. See Capture modes.
  2. Change the migration so pretend can capture it. Often the cleaner fix: move a data backfill out of the migration and into a job the deploy dispatches, or drop an introspection guard that is not actually needed. The migration then captures whole in pretend mode.
  3. Suppress it deliberately. If you have decided a flagged migration is fine — a helper you know is harmless, a pattern you accept — record that decision as a suppression. A suppression is a documented choice, and it is always shown as such in the report, so it never looks like an ordinary clean result.

The one thing you should never do is treat an undetermined as a pass by ignoring it. It is the tool telling you where it could not see; the reasons above are how you give it sight.