Introduction
You push a small change to your Odoo module on Odoo.sh. You bump the version in __manifest__.py from 0.5 to 0.6 β a routine update. Minutes later, the staging build fails. The error? A warning in a completely different module you didn't even touch.
If this sounds familiar, you've run into one of Odoo.sh's most subtle behaviors: cascade upgrades triggered by version bumps. This article breaks down exactly what happens, why it happens, and how to avoid it.
The Problem: A Simple Version Change Breaks Everything
Imagine you have a module called document_manager that handles certificate generation and letter templates. It has a dependency chain like this:
document_manager
βββ hr_connector (depends on document_manager)
βββ hr_connector_charts (depends on hr_connector)
βββ attendance_tracker (depends on hr_connector)You make a code change in document_manager β maybe you refactored a method or added a new field. You bump the version in the manifest. You push to staging.
Build fails.
The error log points to hr_connector, not document_manager. It shows deprecation warnings that have existed in the codebase for months. What happened?
How Odoo.sh Decides What to Upgrade
Here's the key insight most developers miss:
Odoo.sh uses version numbers β not file diffs β to determine cascade upgrades.
When you push to Odoo.sh, it compares the version field in each module's __manifest__.py against what's currently installed. If the version changed, that module gets upgraded. And critically, all modules that depend on it get upgraded too β recursively.
This is different from how code changes are handled. If you modify files in a module without changing its version, Odoo.sh still detects the file changes and updates that specific module. But it does not cascade to dependent modules.
What We Observed
| Change Type | Module Updated? | Dependents Cascaded? | Build Result |
|---|---|---|---|
| No changes | No | No | Pass |
| Code change, no version bump | Yes | No | Pass |
| Version bump only | Yes | Yes | Fail |
| Code change + version bump | Yes | Yes | Fail |
The version bump was the trigger. Not the code.
Why the Cascade Breaks Things
The cascade itself isn't the problem β it's what it reveals.
When hr_connector is loaded at server startup (which happens every time), Odoo reads its models and views but doesn't run the full upgrade pipeline. Issues like deprecation warnings, schema mismatches, or view validation errors sit dormant.
But when a cascade upgrade forces hr_connector through the actual -u (upgrade) process, Odoo runs:
- Model field reconciliation against the database schema
- View XML validation and re-rendering
- Data file re-processing
- Python code re-evaluation with stricter checks
This is when those sleeping issues wake up. Warnings that are silently ignored during startup become test failures during upgrade.
5 Common Issues That Surface During Cascade Upgrades
1. Deprecated API Decorators
# This works at startup but generates DeprecationWarning during upgrade
@api.model
def create(self, vals):
...
# The correct decorator since Odoo 14+
@api.model_create_multi
def create(self, vals_list):
...2. Deprecated Field Attributes
# Old syntax β triggers deprecation warning
name = fields.Char(track_visibility='onchange')
# Modern syntax
name = fields.Char(tracking=True)3. Deprecated View Elements
<!-- Old Kanban syntax -->
<t t-name="kanban-box">
<!-- Modern syntax -->
<t t-name="card">4. Missing Model Attributes
# Missing _description triggers a warning during upgrade
class CustomModel(models.Model):
_name = 'custom.model'
# Should have: _description = 'Custom Model'5. Schema NOT NULL Violations
Fields that were added as required=True but have existing records with NULL values will fail during upgrade reconciliation.
Quick Fix: Skip the Version Bump
The immediate workaround is straightforward: don't bump the version.
Push your code changes without modifying the version field in __manifest__.py. Odoo.sh will still detect the file changes and apply them to your module. Since the version didn't change, no cascade upgrade is triggered, and dependent modules remain untouched.
This is a valid short-term strategy, especially when you need to deploy urgently. But it's not sustainable β you lose version tracking, and eventually you'll need to bump.
Permanent Fix: Clean Up the Dependency Chain
The real solution is to fix every warning in every module in the dependency chain. Here's the systematic approach.
Step 1: Test the Upgrade Locally
Run the upgrade command locally against a copy of your production database:
odoo -d your_database -u hr_connector --stop-after-init --log-level=warningCapture all warnings and errors. This gives you the exact list of issues that Odoo.sh will encounter.
Step 2: Fix Deprecation Warnings
Go through each module in the dependency chain and fix:
@api.modeloncreate()methods β change to@api.model_create_multitrack_visibilityattributes β change totracking=Truereadonly=1in wizards β change toreadonly=True- Missing
_descriptionon models - Deprecated view elements like
kanban-boxβ change tocard
Step 3: Fix Schema Issues
Check for NOT NULL constraint violations:
SELECT column_name, is_nullable
FROM information_schema.columns
WHERE table_name = 'your_table'
AND is_nullable = 'NO';Ensure all required fields have default values or migration scripts.
Step 4: Test the Full Cascade
Once all fixes are in place, bump the version on the root module and push to a test branch. Verify that the full cascade upgrade completes cleanly.
Bonus: Watch Out for Ghost Files
While debugging this issue, we discovered another subtle problem. When trying to revert to a known-good commit using:
git checkout <good-commit> -- .This restores all tracked files to their state at that commit. But it does not delete files that were added in later commits. We found over a dozen "ghost files" β models, wizards, and data files that didn't exist in the good commit but were still present in the working directory.
These ghost files can introduce import errors, duplicate model registrations, or unexpected data during module upgrade.
The safe way to verify a revert:
# Check for files that exist now but didn't in the target commit
git diff <good-commit> HEAD --name-only --diff-filter=A
# These files need to be explicitly removedConclusion
Here are the key takeaways:
- Version bumps trigger cascade upgrades β Odoo.sh upgrades all dependent modules when a dependency's version changes.
- Code changes without version bumps are safe β Odoo.sh still applies file changes but doesn't cascade.
- Dormant issues surface during upgrades β Deprecation warnings and schema issues that are silent during startup become failures during upgrade.
- Fix the full chain before bumping β Clean up all dependent modules before changing any version number.
- Verify reverts completely β
git checkout -- .doesn't delete added files; always check for ghost files.
Understanding how Odoo.sh handles module upgrades saves hours of debugging. The cascade behavior is documented, but its implications β especially around dormant warnings β catch even experienced developers off guard.
Next time your staging build fails after a "simple" version bump, you'll know exactly where to look.