Enable Dark Mode!
how-to-handle-odoo-constraints-and-server-errors-in-mobo.jpg
By: Muhammed Shehzad K

How to Handle Odoo Constraints and Server Errors in Mobo

Technical Flutter mobo

The Problem

Odoo is strict by design. A single missing field, permission issue, or constraint violation can expose raw Python stack traces or generic server exceptions in your Flutter app.

Showing errors like:

odoo.exceptions.UserError: Invalid Quantity

to end users is unacceptable in a production mobile application.

This guide explains how to correctly classify, parse, and handle Odoo server errors in Flutter, turning low-level backend exceptions into clean, user-friendly messages.

1. Understanding the Error Hierarchy

Before handling errors, you must understand what you are actually catching.

Network-Level Errors

  • SocketException / ClientException
    • Cause: No internet, server down, DNS failure
    • User Message: "Server is unreachable. Please check your connection."

Session-Level Errors

  • OdooSessionExpiredException
  • HTTP 403 responses
  • Redirects to /web/login (HTML instead of JSON)

Cause: Session expired or invalid cookies

User Message: "Your session has expired. Please log in again."

RPC / Server-Level Errors (Most Important)

These originate from Python code inside Odoo.

2. Robust Exception Parsing

Exception TypeMeaningRecommended UI Message
odoo.exceptions.UserErrorBusiness logic violationShow exact message
odoo.exceptions.ValidationErrorPython constraintShow exact message
odoo.exceptions.AccessErrorSecurity / ACL rule"Access denied"
psycopg2.IntegrityErrorDB constraint (unique, FK)"Duplicate or invalid record"

Raw exceptions returned by Odoo are noisy and inconsistent. To display meaningful messages, you must extract the real error message using pattern matching.

Centralized Error Parser

class OdooErrorHandler {
  static String parse(Object error) {
    final msg = error.toString();
    // 1. Network errors
    if (msg.contains('SocketException') || msg.contains('Connection refused')) {
      return 'Server is unreachable. Check your internet connection.';
    }
    // 2. Session errors
    if (msg.contains('Session expired') || msg.contains('403')) {
      return 'Session expired. Please log in again.';
    }
    // 3. Odoo Python exceptions
    final strategies = [
      RegExp(r'odoo\.exceptions\.[^:]+:\s*(.+?)(?:\n|$)', multiLine: true),
      RegExp(r'Exception:\s*(.+?)(?:\n|$)', multiLine: true),
      RegExp(r'ValueError:\s*(.+?)(?:\n|$)', multiLine: true),
    ];
    for (final pattern in strategies) {
      final match = pattern.firstMatch(msg);
      if (match != null) {
        return _clean(match.group(1)!);
      }
    }
    return 'Unknown error: ${msg.split('\n').first}';
  }
  static String _clean(String s) {
    return s.trim().replaceAll(RegExp(r'&#?\w+;'), '');
  }
}

Result:

  • Developers still get full stack traces in logs
  • Users only see clear, actionable messages

3. Handling "Database Discovery" Issues

Some Odoo deployments disable database listing using:

list_db = False

In this case, standard RPC database discovery fails, breaking login flows.

Strategy: Scrape the Database Name

If RPC discovery fails, extract the database name from cookies or HTML.

Future<String?> scrapeDatabase(String url) async {
  try {
    final resp = await http.get(Uri.parse('$url/web/login'));
    // 1. Check cookies
    final cookies = resp.headers['set-cookie'] ?? '';
    if (cookies.contains('db=')) {
      return RegExp(r'db=([^;]+)').firstMatch(cookies)?.group(1);
    }
    // 2. Fallback: parse HTML body for Odoo patterns
    // See: OdooApiService._extractDbFromHtml
  } catch (_) {}
  return null;
}

This technique is critical for hosted and security-hardened Odoo instances.

4. Advanced Technique: Dynamic Schema Healing

Odoo schemas evolve. Fields may exist in one version or module but not another.

The Problem

Your Flutter app requests a field that doesn’t exist:

ValueError: Invalid field 'x_mobile'

This crashes the request.

The Solution: Recursive Self-Healing Calls

Detect the offending field, remove it, and retry automatically.

Future<dynamic> safeCall(
  String model,
  String method,
  List args,
  Map kwargs,
) async {
  try {
    return await client.callKw(model, method, args, kwargs);
  } catch (e) {
    final err = e.toString();
    final match = RegExp(r"Invalid field '([^']+)'" ).firstMatch(err);
    if (match != null) {
      final badField = match.group(1);
      print(" Healing schema: Removing '$badField' and retrying...");
      // 1. Remove from read/search fields
      if (kwargs['fields'] is List) {
        kwargs['fields'] = List.from(kwargs['fields'])..remove(badField);
        return safeCall(model, method, args, kwargs);
      }
      // 2. Remove from create/write values
      if (args.isNotEmpty && args.last is Map) {
        final vals = Map.from(args.last);
        if (vals.remove(badField) != null) {
          args.last = vals;
          return safeCall(model, method, args, kwargs);
        }
      }
      // 3. Remove from the order clause
      if (kwargs['order'] is String && kwargs['order'].contains(badField)) {
        final orders = kwargs['order'].split(', ')
          ..removeWhere((o) => o.contains(badField));
        kwargs['order'] = orders.join(', ');
        return safeCall(model, method, args, kwargs);
      }
    }
    rethrow; // Cannot heal further
  }
}

This approach enables a single Flutter codebase to support multiple Odoo versions and module combinations.

Robustness beats visibility

  • Never expose raw Odoo stack traces to users
  • Parse and normalize errors centrally
  • Scrape database names when discovery is disabled
  • Heal schema mismatches dynamically

By handling Odoo constraints and server errors intelligently, your Mobo app becomes resilient, professional, and enterprise-ready—even in hostile or inconsistent backend environments.

To read more about How to Handle Timezones and Date Fields from Odoo for Mobo, refer to our blog How to Handle Timezones and Date Fields from Odoo for Mobo.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, KINFRA Techno Park
Kakkanchery, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message