Validation is a gate — either the data passes or it doesn't, and the reason is usually right there in the error message. Submission is different: by the time `submitForm()` runs, Drupal has already decided your data is good, so if something goes wrong from here, it's not a validation problem — it's your own code failing to save a value, dispatch an event, or update an entity correctly. Debugging that means tracing execution flow, not error messages.
What you'll learn in this lesson
- Why `submitForm()` bugs behave differently from validation bugs, and need a different debugging approach
- How to use `kint()` inside `submitForm()` to inspect exactly what values arrived
- Why `submitForm()` never has a return value or a way to "fail" the way `validateForm()` does
- What Xdebug adds beyond what `kint()` alone can show you
Why submitForm() problems are harder to spot
`submitForm()` only runs after `validateForm()` has already approved the data — Drupal guarantees you won't reach it with invalid input. That's exactly what makes bugs here trickier: there's no error message telling you what went wrong, because as far as Form API is concerned, nothing did. If an entity fails to save, or a redirect doesn't fire, or a value gets processed incorrectly, the form still "succeeds" from Form API's point of view. You have to go looking for the problem yourself.
Inspecting values with kint() inside submitForm()
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class SubmitFormExamination extends FormBase {
public function getFormId() {
return 'submit_form_examination';
}
public function buildForm(array $form, FormStateInterface $form_state) {
$form['username'] = [
'#type' => 'textfield',
'#title' => $this->t('Username'),
'#required' => TRUE,
];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];
return $form;
}
public function submitForm(array &$form, FormStateInterface $form_state) {
kint($form_state->getValues());
$username = $form_state->getValue('username');
$this->messenger()->addMessage($this->t('Submitted username: @name', ['@name' => $username]));
}
}
`kint($form_state->getValues())` dumps every submitted value at the exact moment `submitForm()` starts — before any of your own processing touches it. That's the first thing worth confirming when a submission "doesn't work": did the value you expect actually arrive, in the shape you expect, or is the real problem earlier, in how the field was defined?
When kint() isn't enough: reaching for Xdebug
`kint()` shows you a snapshot of a variable at one specific line. Sometimes that's not enough — you need to watch execution actually happen, step by step, through several method calls (a submit handler calling a service, which calls a repository, which does the actual save). That's what Xdebug is for: setting a real breakpoint inside `submitForm()` lets you step through the entire call chain in your IDE, inspecting every variable at every step, not just the ones you thought to `kint()` in advance.
Quick check: a form submits successfully with no error message, but the entity you expected to be created never shows up. Is this a `validateForm()` bug or a `submitForm()` bug? (Answer: `submitForm()` — validation already passed, so the problem is somewhere in your own save/create logic inside the submit handler, not in the validation rules.)
Key takeaways
- `submitForm()` only runs after validation passes — there's no error message to guide you, so debugging here means tracing execution, not reading a validation failure.
- `kint($form_state->getValues())` at the top of `submitForm()` confirms exactly what data actually arrived, before your own code processes it.
- Unlike `validateForm()`, `submitForm()` has no built-in way to "fail" — any error handling you want has to be written by hand.
- Reach for Xdebug breakpoints, not just `kint()`, when you need to trace execution across multiple method calls rather than inspect one variable at one line.
Coming up next
Submission bugs get even trickier once AJAX enters the picture, since the request/response cycle and the callback that runs afterward add a whole extra layer to trace. The next lesson covers debugging AJAX callbacks specifically — what to check when the page updates incorrectly, or doesn't update at all.