Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
CodeIgniterTroubleshooting guide

CodeIgniter 4 validation not working? 9 causes and fixes

Fix CodeIgniter 4 validation problems fast: wrong input source, lost errors after redirect, permit_empty, strict JSON rules, is_unique placeholders and more.

Cloudsoft Zone

6 min read

Illustration of a woman ticking checkboxes on a large smartphone form, with a potted plant beside it on an orange background
Photograph: Zuko.io Images / CC BY 2.0, via Wikimedia Commons
In this article

When CodeIgniter 4 validation "does not work", the library is almost always doing exactly what it was told, just with different data or rules than you thought. The usual culprits are validating the wrong input source, losing errors on redirect, optional fields without permit_empty, strict type checks on JSON, and model validation quietly skipping rules during updates.

Below are nine causes we see again and again, each with the symptom, the reason and the fix. Everything applies to CodeIgniter 4.7 and matches the current user guide; where behaviour changed in an earlier 4.x release, the version is noted.

Quick diagnosis table

SymptomLikely causeSection
Valid data fails, or invalid data passesValidating a different input source1
Errors never appear in the formRedirect without withInput()2
Empty optional field fails valid_emailNo permit_empty3
JSON request fails in_list with a correct-looking valueStrict rules and types4
Second run returns old errorsNo reset()5
is_unique fails when saving an unchanged recordPlaceholder not replaced6
required ignored on updateModel cleans rules for absent fields7
File rules behave oddlyMixing general rules with file rules8
"Rule not found" exceptionCustom rule class not registered9
Screenshot of a Create Invoice web form with required invoice, client and item fields marked by asterisks
Required and optional fields need different rules: start optional ones with permit_empty.

1. You are validating different data than you think

Symptom: a form value is clearly there but validation fails, or a field you did not send passes. Cause: the older controller method $this->validate() and the library's withRequest() read input with $request->getVar(), which merges $_GET, $_POST and cookies, and for JSON or PUT requests reads the body instead. The user guide now marks both as backward-compatibility only and warns that a cookie can override a POST value of the same name.

Fix: collect the data yourself and use validateData() (available since 4.2), then read the result with getValidated() (since 4.4), which returns only the fields that had rules:

PHP
public function store()
{
    $rules = [
        'name'  => 'required|max_length[100]',
        'email' => 'required|max_length[254]|valid_email',
        'phone' => 'permit_empty|max_length[20]',
    ];

    $data = $this->request->getPost(array_keys($rules));

    if (! $this->validateData($data, $rules)) {
        return redirect()->back()->withInput();
    }

    $valid = $this->validator->getValidated();
    model('ContactModel')->insert($valid);

    return redirect()->to('/contacts')->with('message', 'Saved');
}

For a JSON API, use $this->request->getJSON(true) ?? [] as the data instead of getPost().

2. Errors disappear after a redirect

Symptom: validation fails, the user is sent back to the form, and no messages appear. Cause: PHP shares nothing between requests, so the errors from the failed request are gone. Fix: redirect with redirect()->back()->withInput(), which stores the errors and old input in the session, and display them with the form helper functions validation_list_errors(), validation_show_error('email') or validation_errors(). Load the helper with protected $helpers = ['form']; in the controller, and use set_value() or old() to refill fields.

3. Optional fields fail format rules

Symptom: an empty, optional phone or website field fails with "must contain a valid URL". Cause: format rules do not permit an empty string. Also, a field that is missing from the data is treated as null, not skipped. Fix: start optional fields with permit_empty. It accepts an empty string, null, false or an empty array and skips the remaining rules, except required_with and required_without. If you need to know that a key was sent at all, use field_exists (since 4.5); to validate a key only when it is present, use if_exist.

4. JSON values fail because rules are strict

Symptom: a JSON body with "active": true fails in_list[0,1]. Cause: since 4.3, new projects use the Strict Rules classes, which do not convert types implicitly. Strict in_list accepts strings, integers and floats, but a boolean is neither, so it fails. That is deliberate: the Traditional Rules would turn true into '1' and let it pass, which the user guide calls out as a reason not to use them for JSON.

Fix: agree on the wire format (send 0 or 1), or normalise the value before validating, and keep the strict rules. Do not switch Config\Validation::$ruleSets back to the traditional classes to make a test pass.

Illustration of a clipboard checklist with every box ticked and a blue pen lying across it
Reset the shared validation service between runs, or old errors carry over.

5. A second validation run returns old errors

Symptom: in a loop or an import job, every record after the first bad one fails. Cause: the validation service is shared, and run() does not reset error state. Fix: call reset() before each run, and set the rules again, because reset() clears data, rules and custom messages too:

PHP
$validation = service('validation');

foreach ($rows as $row) {
    $validation->reset();
    $validation->setRules($importRules);

    if (! $validation->run($row)) {
        $failures[] = ['row' => $row, 'errors' => $validation->getErrors()];
    }
}

6. is_unique fails when editing a record

Symptom: saving a user without changing their email fails with "must contain a unique value". Cause: the rule must ignore the current row, usually with a placeholder: is_unique[users.email,id,{id}]. Since 4.3.5, a placeholder is only replaced if the placeholder field itself has a validation rule and passes it, a change made for security. If id has no rule, or is missing from the data, {id} is never replaced.

Fix: give id a rule and include it in the data being validated. With a model, update($id, $data) validates only $data, so add the key yourself; it is removed again before the query because the primary key is not an allowed field:

PHP
protected $validationRules = [
    'id'    => 'max_length[19]|is_natural_no_zero',
    'email' => 'required|max_length[254]|valid_email|is_unique[users.email,id,{id}]',
];

Then, in the controller, pass the key along with the changes: $userModel->update($id, ['id' => $id, 'email' => $email]).

Also watch for spaces: rule strings must not contain spaces between parameters, so is_unique[users.email, id, {id}] will not work.

7. required is ignored when a model updates

Symptom: $model->update($id, ['title' => 'New']) succeeds although another field is required. Cause: by default, $cleanValidationRules is true, so on updates the model temporarily removes rules for fields that are not in the data. That makes partial updates possible, but required rules and rules that depend on other fields are not checked. Fix: if an update must be complete, call $model->cleanRules(false)->update($id, $data) or set the property to false. Remember too that in-model validation runs just before saving; anything you do with the data earlier is unvalidated.

8. File rules misbehave

Symptom: an optional avatar field fails, or a bad file passes. Cause: file rules check $_FILES, not the normal data array, and adding general rules such as permit_empty to a file field stops them working correctly. The field name is also repeated inside every file rule: is_image[avatar]. Fix: keep file fields to file rules only (uploaded, max_size, is_image, mime_in, ext_in, max_dims, min_dims), validate with validateData([], $rules), and upgrade to 4.7.5, which fixed two upload validation bypasses, including one in multiple-file fields.

9. A custom rule is not found

Symptom: an exception says your rule does not exist. Cause: rule classes must be listed in Config\Validation::$ruleSets. Fix: add your class, for example \App\Validation\AccountRules::class, next to the framework's rule classes, and make each rule method return a boolean. For one-off checks, the library also accepts closures and callables as rules.

Gotchas and next steps

  • Validation never changes data. Trim, cast or normalise before you validate, and save the output of getValidated(), not the raw request.
  • Labels make better messages: use ['label' => 'Email address', 'rules' => '...'] instead of a bare rule string.
  • Arrays use dot syntax: contacts.*.email validates every contact's email.
  • Escape errors in custom views with esc(), as the user guide's templates do.

When validation still surprises you, dump three things side by side: the exact array you validated, the rules after placeholders, and $this->validator->getErrors(). In nearly every case one of them is not what you expected, and the fix is a line or two.

Frequently asked questions

  • Should I use validate() or validateData() in a CodeIgniter 4 controller?

    Use validateData(). The user guide marks validate() as backward-compatibility only because it reads input with getVar(), which mixes GET, POST and cookie values.

  • Why are my validation errors empty after redirect()->back()?

    Errors only survive a redirect if you call withInput(), which stores them in the session. Then show them with validation_list_errors() or validation_show_error() from the form helper.

  • How do I make a field optional but validated when filled?

    Put permit_empty first, for example permit_empty|valid_url_strict. Empty values then skip the other rules, except required_with and required_without.

  • Why does is_unique fail when I update a record?

    The rule must exclude the current row with a placeholder such as is_unique[users.email,id,{id}], and since 4.3.5 the id field needs its own rule and must be present in the validated data, or the placeholder is not replaced.

  • What is the difference between strict and traditional validation rules?

    Strict Rules, the default since 4.3, do not convert types implicitly, so a JSON boolean is not treated as the string 1. Traditional Rules exist only for backward compatibility and should not be used in new projects.

  • Why does my model skip required rules on update?

    $cleanValidationRules defaults to true, removing rules for fields absent from the update data. Call cleanRules(false) before update() when the whole record must be validated.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Validation — CodeIgniter Foundation
  2. Controllers: Validating Data — CodeIgniter Foundation
  3. Using CodeIgniter's Model: In-Model Validation — CodeIgniter Foundation
  4. Form Helper — CodeIgniter Foundation
  5. Version 4.7.5 change log — CodeIgniter Foundation, 2026-10-09

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.