💣

mb_strtoupper(): Argument #1 ($string) must be of type string ...

2022/10/24に公開

はい。1時間のスタック。原因はうんこみたいなヤツでした。
Laravel でのイージーミスを経験。同じ経験が二度と生まれませんように。

要は。

  • Laravel の言語ファイル関連のエラー
  • Form Request クラスを疑ってください。
  • そして attributes メソッドを使っている場合、言語ファイルを使った属性名指定の箇所を見てください。
  • 言語定義で「キーに対応した文字列」ではなく、配列やオブジェクトを定義しちゃって、それを呼び出していないかを確認してください。

あらまし

エラーメッセージ

#message: "mb_strtoupper(): Argument #1 ($string) must be of type string, array given"
#code: 0
#file: "./vendor/laravel/framework/src/Illuminate/Support/Str.php"
#line: 965

Argument #1 ($string) must be of type string, array given ということなんですけど、どこから???という話だったわけです。

発生したスタックトレース

こんな感じでした。
これだけみると、ユーザー任意に組み込んだもののエラーではないような気がする。

[2022-10-24 18:26:29] local.ERROR: mb_strtoupper(): Argument #1 ($string) must be of type string, array given {"userId":"******","exception":"[object] (TypeError(code: 0): mb_strtoupper(): Argument #1 ($string) must be of type string, array given at /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Str.php:965)
[stacktrace]
#0 /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Str.php(965): mb_strtoupper(Array, 'UTF-8')
#1 /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Concerns/FormatsMessages.php(323): Illuminate\\Support\\Str::upper(Array)
#2 /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Concerns/FormatsMessages.php(244): Illuminate\\Validation\\Validator->replaceAttributePlaceholder(':attribute\\xE3\\x81\\xAF\\xE5\\xBF...', Array)
#3 /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Validator.php(885): Illuminate\\Validation\\Validator->makeReplacements(':attribute\\xE3\\x81\\xAF\\xE5\\xBF...', 'catch_images', 'Required', Array)
#4 /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Validator.php(612): Illuminate\\Validation\\Validator->addFailure('catch_images', 'Required', Array)
#5 /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Validator.php(417): Illuminate\\Validation\\Validator->validateAttribute('catch_images', 'Required')
#6 /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Validator.php(448): Illuminate\\Validation\\Validator->passes()
#7 /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/ValidatesWhenResolvedTrait.php(31): Illuminate\\Validation\\Validator->fails()
#8 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Providers/FormRequestServiceProvider.php(30): Illuminate\\Foundation\\Http\\FormRequest->validateResolved()
#9 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(1267): Illuminate\\Foundation\\Providers\\FormRequestServiceProvider->Illuminate\\Foundation\\Providers\\{closure}(Object(App\\Http\\Requests\\Article\\StoreRequest), Object(Illuminate\\Foundation\\Application))
#10 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(1232): Illuminate\\Container\\Container->fireCallbackArray(Object(App\\Http\\Requests\\Article\\StoreRequest), Array)
#11 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(1217): Illuminate\\Container\\Container->fireAfterResolvingCallbacks('App\\\\Http\\\\Reques...', Object(App\\Http\\Requests\\Article\\StoreRequest))
#12 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(776): Illuminate\\Container\\Container->fireResolvingCallbacks('App\\\\Http\\\\Reques...', Object(App\\Http\\Requests\\Article\\StoreRequest))
#13 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(860): Illuminate\\Container\\Container->resolve('App\\\\Http\\\\Reques...', Array, true)
#14 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(692): Illuminate\\Foundation\\Application->resolve('App\\\\Http\\\\Reques...', Array)
#15 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(845): Illuminate\\Container\\Container->make('App\\\\Http\\\\Reques...', Array)
#16 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php(85): Illuminate\\Foundation\\Application->make('App\\\\Http\\\\Reques...')
#17 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php(50): Illuminate\\Routing\\ControllerDispatcher->transformDependency(Object(ReflectionParameter), Array, Object(stdClass))
#18 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php(30): Illuminate\\Routing\\ControllerDispatcher->resolveMethodDependencies(Array, Object(ReflectionMethod))
#19 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(60): Illuminate\\Routing\\ControllerDispatcher->resolveClassMethodDependencies(Array, Object(App\\Http\\Controllers\\Api\\V1\\ArticleController), 'store')
#20 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(40): Illuminate\\Routing\\ControllerDispatcher->resolveParameters(Object(Illuminate\\Routing\\Route), Object(App\\Http\\Controllers\\Api\\V1\\ArticleController), 'store')
#21 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(App\\Http\\Controllers\\Api\\V1\\ArticleController), 'store')
#22 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()
#23 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(725): Illuminate\\Routing\\Route->run()
#24 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#25 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#26 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#27 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(126): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#28 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(102): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequest(Object(Illuminate\\Http\\Request), Object(Closure), Array)
#29 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(54): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequestUsingNamedLimiter(Object(Illuminate\\Http\\Request), Object(Closure), 'api', Object(Closure))
#30 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\ThrottleRequests->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'api')
#31 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#32 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(726): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#33 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(703): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#34 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(667): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#35 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(656): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#36 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(190): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#37 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#38 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#39 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#40 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#41 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#42 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#43 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#44 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#45 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#46 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#47 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#48 /var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(62): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#49 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#50 /var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#51 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#52 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#53 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(165): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#54 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(134): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#55 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(545): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#56 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(511): Illuminate\\Foundation\\Testing\\TestCase->call('POST', 'http://hoge.fuga...', Array, Array, Array, Array, '[]')
#57 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(353): Illuminate\\Foundation\\Testing\\TestCase->json('POST', 'http://hoge.fuga...', Array, Array)
#58 /var/www/html/tests/Feature/Api/V1/Article/StoreTest.php(68): Illuminate\\Foundation\\Testing\\TestCase->postJson('http://hoge.fuga...')
#59 [internal function]: P\\Tests\\Feature\\Api\\V1\\Article\\StoreTest->{closure}()
#60 /var/www/html/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(151): call_user_func(Object(Closure))
#61 [internal function]: P\\Tests\\Feature\\Api\\V1\\Article\\StoreTest->Pest\\Factories\\{closure}()
#62 /var/www/html/vendor/pestphp/pest/src/Concerns/Testable.php(299): call_user_func_array(Object(Closure), Array)
#63 /var/www/html/vendor/pestphp/pest/src/Support/ExceptionTrace.php(29): P\\Tests\\Feature\\Api\\V1\\Article\\StoreTest->Pest\\Concerns\\{closure}()
#64 /var/www/html/vendor/pestphp/pest/src/Concerns/Testable.php(300): Pest\\Support\\ExceptionTrace::ensure(Object(Closure))
#65 /var/www/html/vendor/pestphp/pest/src/Concerns/Testable.php(276): P\\Tests\\Feature\\Api\\V1\\Article\\StoreTest->__callClosure(Object(Closure), Array)
#66 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(1548): P\\Tests\\Feature\\Api\\V1\\Article\\StoreTest->__test()
#67 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(1154): PHPUnit\\Framework\\TestCase->runTest()
#68 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php(728): PHPUnit\\Framework\\TestCase->runBare()
#69 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(904): PHPUnit\\Framework\\TestResult->run(Object(P\\Tests\\Feature\\Api\\V1\\Article\\StoreTest))
#70 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestSuite.php(673): PHPUnit\\Framework\\TestCase->run(Object(PHPUnit\\Framework\\TestResult))
#71 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestSuite.php(673): PHPUnit\\Framework\\TestSuite->run(Object(PHPUnit\\Framework\\TestResult))
#72 /var/www/html/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(673): PHPUnit\\Framework\\TestSuite->run(Object(PHPUnit\\Framework\\TestResult))
#73 /var/www/html/vendor/phpunit/phpunit/src/TextUI/Command.php(144): PHPUnit\\TextUI\\TestRunner->run(Object(PHPUnit\\Framework\\TestSuite), Array, Array, false)
#74 /var/www/html/vendor/pestphp/pest/src/Console/Command.php(117): PHPUnit\\TextUI\\Command->run(Array, false)
#75 /var/www/html/vendor/pestphp/pest/bin/pest(62): Pest\\Console\\Command->run(Array)
#76 /var/www/html/vendor/pestphp/pest/bin/pest(63): {closure}()
#77 /var/www/html/vendor/bin/pest(120): include('/var/www/html/v...')
#78 {main}
"} 

原因のコード

言語ファイル側に指定していたキー名に対応する文字列を「配列」で定義していたことで、マッピングが効かなくなったようです。ただ、元となる言語ファイルのパスはもちろん、呼び出しているForm Requestクラスの所在も明らかになっていないので、特定にかなり時間がかかりました・・・

言語ファイル

# resources/lang/ja/hoge.php
return [

// ...

    'catch_images' => [
        'extention' => '拡張子',
        'data_base64' => '画像データ',
    ],
    
// ...

言語ファイルを使っていたForm Requestクラス

# app/Http/Requests/HogeRequest.php

// ...

    /**
     * Get custom attributes for validator errors.
     *
     * @return array
     */
    public function attributes()
    {
        return [
	    // ↓ これがダメ
            'catch_image' => __('article.item.catch_images'),

            'catch_images.*.extention' => __('article.item.catch_images.extention'),
            'catch_images.*.data_base64' => __('article.item.catch_images.data_base64'),
        ];
    }

// ...

カプセルメソッド系のエラーは結構きつい

Laravel はいい感じにカプセルしてくれているから、ビジネスロジックとしては気にしなくて良く安心 .. と思いきや、実装中やユニットテストで発生したりするので、遭遇しちゃうとかなりきつい・・

Discussion