{"record":{"id":"c71ee86a80dfc8cf","repo":"pestphp/pest","slug":"expectation-value-length-is-not-countable","errorCode":null,"errorMessage":"Expectation value length is not countable.","messagePattern":"Expectation value length is not countable\\.","errorType":"exception","errorClass":"BadMethodCallException","httpStatus":null,"severity":"error","filePath":"src/Mixins/Expectation.php","lineNumber":240,"sourceCode":"        if (is_string($this->value)) {\n            Assert::assertEquals($number, mb_strlen($this->value), $message);\n\n            return $this;\n        }\n\n        if (is_iterable($this->value)) {\n            return $this->toHaveCount($number, $message);\n        }\n\n        if (is_object($this->value)) {\n            $array = method_exists($this->value, 'toArray') ? $this->value->toArray() : (array) $this->value;\n\n            Assert::assertCount($number, $array, $message);\n\n            return $this;\n        }\n\n        throw new BadMethodCallException('Expectation value length is not countable.');\n    }\n\n    /**\n     * @return self<TValue>\n     */\n    public function toHaveCount(int $count, string $message = ''): self\n    {\n        if (! is_countable($this->value) && ! is_iterable($this->value)) {\n            InvalidExpectationValue::expected('countable|iterable');\n        }\n\n        Assert::assertCount($count, $this->value, $message);\n\n        return $this;\n    }\n\n    /**\n     * @param  Countable|iterable<mixed>  $expected","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/pestphp/pest/blob/1af74a215c3a89bc7a44fc6f9322e369eaad2423/src/Mixins/Expectation.php#L222-L258","documentation":"toHaveLength() supports three value kinds: strings (mb_strlen comparison), iterables (delegated to toHaveCount), and objects (toArray() if present, else cast to array and counted). For anything else — int, float, bool, null — there is no meaningful length, so Pest throws BadMethodCallException instead of guessing. Note the sibling toHaveCount() has its own stricter guard for countable|iterable values.","triggerScenarios":"expect(42)->toHaveLength(2); expect(null)->toHaveLength(0); expect(3.14)->toHaveLength(4); expect(true)->toHaveLength(...); passing a value that a producer typed as int (strlen result, count result) when a string was intended.","commonSituations":"Counting digits and forgetting to cast: expect(12345)->toHaveLength(5) instead of expect((string) 12345); null returned by a lookup fed directly into the chain; strict-types codebases where an int sneaks past reviews; numbers coming from APIs as int rather than numeric strings.","solutions":["Cast to string when you mean character length: expect((string) $value)->toHaveLength(5).","If you mean item count, keep the value iterable/countable and use toHaveCount() instead.","Handle null separately (expect($value)->not->toBeNull()) before asserting length.","Fix the producer to return the string form (e.g., (string) $id) when length semantics matter."],"exampleFix":"// before\nexpect($zipCode)->toHaveLength(5); // $zipCode is int 12345\n// after\nexpect((string) $zipCode)->toHaveLength(5);\n// or fix the producer to keep it a string","handlingStrategy":"type-guard","validationCode":"$value = $api->zipCode(); // could be int|string\nif (is_int($value)) {\n    $value = (string) $value;\n}\nexpect($value)->toHaveLength(5);","typeGuard":"function hasLength(mixed $value): bool\n{\n    return is_string($value) || is_iterable($value) || is_object($value);\n}\n\n// usage\nif (hasLength($value)) {\n    expect($value)->toHaveLength(5);\n} else {\n    expect($value)->toBeNull(); // or handle scalar case explicitly\n}","tryCatchPattern":null,"preventionTips":["Cast numeric values to string when asserting character length: expect((string) $value)->toHaveLength(n).","Null-check producers before length assertions.","Use toHaveCount() for collections and toHaveLength() for strings to keep type intent explicit."],"tags":["pest","expectation","to-have-length","countable","type-error"],"backgroundTag":"type-not-countable","analyzedSha":"1af74a215c3a89bc7a44fc6f9322e369eaad2423","analyzedAt":"2026-08-21T02:16:38.132Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}