04. トラブルシューティング
このドキュメントの目的
開発中によく遭遇するエラーと、その即座解決方法を提供します。
所要時間
⏱️ 約15分(参照用ドキュメント - 必要時に該当箇所を参照)
エラーカテゴリ
| カテゴリ | 説明 |
|---|---|
| ビルドエラー | コンパイル失敗・依存関係エラー |
| 実行時エラー | NullPointerException・ClassCastException等 |
| データベースエラー | SQL実行失敗・トランザクションエラー |
| 認証・認可エラー | 403 Forbidden・401 Unauthorized |
| テストエラー | E2Eテスト失敗 |
ビルドエラー
エラー1: spotlessCheck 失敗
エラーメッセージ:
> Task :libs:idp-server-use-cases:spotlessJavaCheck FAILED
The following files had format violations:
libs/idp-server-use-cases/src/main/java/...
原因: コードフォーマットが規約に準拠していない
解決策:
./gradlew spotlessApply
予防策: コミット前に必ず実行
エラー2: Cannot resolve symbol
エラーメッセージ:
error: cannot find symbol
symbol: class TenantQueryRepository
location: package org.idp.server.platform.multi_tenancy.tenant
原因: 依存モジュールがbuild.gradleに追加されていない
解決策:
// libs/idp-server-use-cases/build.gradle
dependencies {
implementation project(':libs:idp-server-platform')
implementation project(':libs:idp-server-core')
implementation project(':libs:idp-server-control-plane')
}
確認コマンド:
./gradlew :libs:idp-server-use-cases:dependencies
エラー3: Circular Dependency(循環依存)
エラーメッセージ:
Circular dependency between the following tasks:
:libs:idp-server-core:compileJava
:libs:idp-server-use-cases:compileJava
原因: モジュール間の依存関係が循環している
解決策: アーキテクチャの依存方向を修正
✅ 正しい依存方向:
Controller → UseCase → Core → Adapter
❌ 間違い(循環依存):
Core → UseCase → Core
修正方法: 共通コードをidp-server-platformに移動
実行時エラー
エラー4: NullPointerException in AuditLogPublisher
エラーメッセージ:
java.lang.NullPointerException: Cannot invoke "RequestAttributes.toMap()" because "requestAttributes" is null
at AuditLogCreator.create(AuditLogCreator.java:45)
原因: RequestAttributesがControllerから渡されていない
解決策: Controllerで@RequestAttributeを追加
@GetMapping("/{tenantId}")
public ResponseEntity<?> get(
@PathVariable("tenantId") String tenantId,
@AuthenticationPrincipal User operator,
@RequestAttribute OAuthToken oAuthToken,
@RequestAttribute RequestAttributes requestAttributes) { // ✅ 追加
// ...
}
エラー5: TenantNotFoundException
エラーメッセージ:
org.idp.server.platform.multi_tenancy.tenant.TenantNotFoundException:
Tenant not found: 18ffff8d-xxxx-xxxx-xxxx-xxxxxxxxxxxx
原因: 存在しないテナントIDを指定している
解決策:
開発環境
# テナント作成
curl -X POST http://localhost:8080/v1/management/tenants \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "test-tenant",
"display_name": "Test Tenant"
}'
E2Eテスト
beforeAll(async () => {
// テナント作成
const tenantResponse = await axios.post(
'http://localhost:8080/v1/management/tenants',
{ name: 'test-tenant', display_name: 'Test Tenant' },
{ headers: { Authorization: `Bearer ${adminToken}` } }
);
tenantId = tenantResponse.data.tenant_id;
});
エラー6: ClassCastException: Map cannot be cast to String
エラーメッセージ:
java.lang.ClassCastException: class java.util.HashMap cannot be cast to class java.lang.String
at ClientConfigurationMapper.map(ClientConfigurationMapper.java:23)
原因: JSONB列のデータ型誤り
解決策: JsonConverterを使用
// ❌ 間違い: 直接キャスト
String metadata = (String) row.get("metadata");
// ✅ 正しい: JsonConverter使用
JsonConverter converter = JsonConverter.snakeCaseInstance();
Map<String, Object> metadata = converter.read((String) row.get("metadata"));
データベースエラー
エラー7: PSQLException: relation "xxx" does not exist
エラーメッセージ:
org.postgresql.util.PSQLException: ERROR: relation "client_configuration" does not exist
原因: データベースマイグレーションが未実行
解決策:
# Flywayマイグレーション実行
./gradlew flywayMigrate
# または、アプリケーション起動時に自動実行される
./gradlew bootRun
確認コマンド:
# 適用済みマイグレーション確認
./gradlew flywayInfo
エラー8: TransactionRequiredException
エラーメッセージ:
org.idp.server.platform.datasource.TransactionRequiredException:
Transaction is required for this operation
原因: EntryServiceに@Transactionアノテーションがない
解決策:
// ❌ 間違い: @Transactionなし
public class ClientManagementEntryService implements ClientManagementApi {
// ...
}
// ✅ 正しい: @Transaction付与
@Transaction
public class ClientManagementEntryService implements ClientManagementApi {
// ...
}
エラー9: Row Level Security (RLS) エラー
エラーメッセージ:
org.postgresql.util.PSQLException: ERROR: new row violates row-level security policy for table "client_configuration"
原因: app.tenant_idが設定されていない状態でINSERT/UPDATE
解決策: TransactionManager.setTenantId()を使用
// ✅ 正しい: Repository実装でTenant設定
@Override
public void register(Tenant tenant, ClientConfiguration configuration) {
TransactionManager.setTenantId(tenant.identifier().value());
String sql = "INSERT INTO client_configuration (tenant_id, client_id, ...) VALUES (?, ?, ...)";
sqlExecutor.execute(sql, tenant.identifier().value(), configuration.clientId().value(), ...);
}
認証・認可エラー
エラー10: 403 Forbidden - Permission Denied
エラーメッセージ:
{
"error": "access_denied",
"error_description": "permission denied required permission [tenant:write], but [tenant:read]"
}
原因: 必要な権限がないトークンでAPIを呼び出している
解決策: