The Challenge
Client: UDekom Balkans
Website: https://balkans-jetss.org
Problem: Scientific journal on OJS 3.3.0.9 with non-functional DOI assignment
Udekom Balkans contacted us in need of assistance. Their academic journal’s DOI (Digital Object Identifier) assignment system had stopped working—a critical issue for any scientific publication. Citations, indexing, and academic credibility all depend on functional DOIs.
After researching online forums, they found the common recommendation: “Just upgrade to the latest OJS version.” They reached out to us, hoping for a straightforward solution.
What we discovered would prove this advice dangerously wrong.
NOTICE: We do not claim that this is entirely fault of the OJS and PKP, this is the interpretation of our experience that may be highly subjective. The purpose of this post is to help someone who may experience similar problems and to point out some possible areas of improvements for the OJS.
Attempt #1: The “Direct Upgrade from OJS 3.3.x to 3.5.x” Disaster
Trying OJS 3.3.0.9 → 3.5.0 (The “Happy Path”)
Following PKP’s official documentation, we attempted a direct upgrade to OJS 3.5.0, the latest stable release at the time.
Result: Complete failure.
Technical Problems Encountered:
1. Database Schema Mismatch
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'at.assoc_type' in 'WHERE'
The upgrade script assumed database columns that didn’t exist in the 3.3.0.9 schema.
2. Missing Configuration Values
[email]
; Missing: default = sendmail
; Missing: smtp_server configuration
; Missing: encryption key in [security] section
Critical config.inc.php values were missing, causing the upgrade to fail silently.
3. Circular Table Dependencies
ERROR: Cannot rename 'notification_subscription_settings.context_id'
to 'context' - column 'context' already exists
The migration script tried to:
- Create column
context - Copy data from
context_idtocontext - Drop
context_id - Rename
contexttocontext_id
This created an infinite loop, with the script fighting against itself.
4. No Rollback Mechanism
When migrations failed halfway through:
❌ Tables left in inconsistent state
❌ Some migrations applied, others not
❌ No automatic rollback
❌ Manual database restoration required
5. Index Name Conflicts
SQLSTATE[42000]: Duplicate key name 'publications_doi_id'
Upgrade scripts attempted to create indexes that already existed from previous failed attempts.
6. Foreign Key Constraint Violations
ERROR: Cannot add foreign key constraint
'notification_subscription_settings_context_foreign'
```
Data integrity issues prevented foreign key creation.
**Decision:** Abort and restore from backup. Direct upgrade to 3.5.0 is **impossible** from 3.3.0.9.
---
## The ONLY Solution: Version-by-Version Migration
After analyzing the codebase and migration scripts, we realized **there is no shortcut with OJS upgrades.**
### Our Methodology
We performed **incremental upgrades** through every single minor version:
**Upgrade Path:**
```
3.3.0.9 → 3.3.0.10 → 3.3.0.11 → 3.3.0.12 → 3.3.0.13 → 3.3.0.14
→ 3.3.0.15 → 3.3.0.16 → 3.4.0.0 → 3.4.0.1 → 3.4.0.2 → ...
→ 3.4.0.10 ✓
Attempt #2: The “OJS Incremental Upgrade”
Process for Each Version:
1. Pre-Upgrade Backup
# Database snapshot
mysqldump -h "hostname" -u "username" -p \\
db_name| gzip > \\
backup_$(date +%Y%m%d_%H%M).sql.gz
# File system backup
tar -czf public_html_backup_$(date +%Y%m%d_%H%M).tar.gz public_html/
2. Manual Database Fixes
At several versions, we had to manually create missing structures:
-- Example: Missing 'redirect' column in 3.4.0.2
ALTER TABLE site ADD COLUMN IF NOT EXISTS redirect BIGINT DEFAULT NULL;
-- Example: Missing 'assoc_type' in announcement_types
ALTER TABLE announcement_types
ADD COLUMN IF NOT EXISTS assoc_type BIGINT(20) DEFAULT NULL AFTER context_id,
ADD COLUMN IF NOT EXISTS assoc_id BIGINT(20) DEFAULT NULL AFTER assoc_type;
-- Example: Fixing email_templates_default_data structure
ALTER TABLE email_templates_default_data
ADD COLUMN IF NOT EXISTS name TEXT DEFAULT NULL AFTER locale;
3. Config File Updates
Critical config.inc.php values were missing, causing the upgrade to fail silently.
# Add missing email configuration
echo "default = sendmail" >> config.inc.php
# Verify encryption key exists
grep "encryption =" config.inc.php || \\
echo "encryption = [generated_key]" >> config.inc.php
4. Upgrade Execution
php tools/upgrade.php upgrade
5. Post-Upgrade Validation
✓ Test user login
✓ Test article submission workflow
✓ Verify file uploads
✓ Check database integrity
✓ Review error logs
6. Cache Clearing
rm -rf cache/*
Result: Successfully reached OJS 3.4.0.10!
Why Migration from OJS 3.4 to OJS 3.5 is Currently Impossible
Critical Bugs in 3.4.x → 3.5.0 Migration
We attempted the final jump to 3.5.0 after stabilizing on 3.4.0.10.
Blockers Encountered:
Error #1: Circular Column Rename Loop
SQLSTATE[42S21]: Column 'site.redirect_context_id' already exists
Migration script logic:
1. ALTER TABLE site ADD COLUMN redirect
2. UPDATE site SET redirect = redirect_context_id
3. ALTER TABLE site DROP COLUMN redirect_context_id
4. ALTER TABLE site CHANGE redirect redirect_context_id
Problem: Step 4 fails because redirect_context_id still exists
Error #2: Notification Settings Table Deadlock
SQLSTATE[42S22]: Column 'context' not found
SQLSTATE[42S01]: Column 'context' already exists
The script simultaneously tries to:
- Create 'context' column
- Drop 'context_id' column
- Rename 'context' to 'context_id'
This creates an unresolvable state.
Error #3: Email Template Migration Failure
SQLSTATE[42000]: Syntax error in SQL statement
Attempted to use double quotes in VALUES clause
```
**Conclusion:** OJS 3.5.0 migration from 3.4.x has **unresolved bugs** as of February 2026. We recommend staying on 3.4.0.10 until PKP releases patches.
---
## Problem #2: DOI Management Still Broken
After successfully reaching 3.4.0.10, we expected DOI functionality to work.
**It didn't.**
### Error #1: "An unexpected error has occurred"
When clicking on any article in the DOI management page:
```
[Browser displays]
An unexpected error has occurred. Please reload the page and try again.
[PHP error log shows]
SQLSTATE[42S22]: Column not found: 1054
Unknown column 'current_p.doi_id' in 'WHERE'
```
**Root Cause:** The `publications` table was missing the `doi_id` column entirely.
### Error #2: "Some DOI(s) could not be updated"
When attempting to assign DOIs:
```
[UI shows generic error]
Some DOI(s) could not be updated
[Network tab reveals actual errors]
{"doi": ["This is not formatted correctly."]}
{"doi": ["The given DOI suffix is already in use for another published item."]}
Root Cause: Multiple database structural issues.
Fixing DOI problems on OJS 3.4.X
After setting up 3.4.X version problems with DOI still persisted.
4 major problems occured:
1. Opening DOI screen showed error mesage: “An unexpected error has occured. Please reload the page and try again.”
- 2. Attempting to edit DOI for a single publication over DOI field showed error message: “Some DOI(s) could not be updated”
- 3. Previously entered publication DOI vanished
- 4. PDF for publications were missing.
Solution FOR DOI problems on OJS 3.4.x
To solve these problems following path was taken:
OJS 3.4 introduced a completely new DOI architecture:
Old system (OJS 3.3):
- DOIs stored in
publication_settingstable - Setting name:
pub-id::doi - No dedicated DOI table
New system (OJS 3.4):
- Dedicated
doistable - Dedicated
doi_settingstable publications.doi_idforeign key column- Link table structure
The upgrade script failed to:
- Create the
doistable - Create the
doi_settingstable - Add
doi_idcolumn topublications - Migrate legacy DOIs to new structure
Missing PDF happened because migration manuals on the web did not mention to copy the uploaded files folder, they were later copied from the backup.
The Fix: Complete SQL Migration
Step 1: Create Missing Tables
S-- Create dois table
CREATE TABLE IF NOT EXISTS dois (
doi_id BIGINT NOT NULL AUTO_INCREMENT,
context_id BIGINT NOT NULL,
doi VARCHAR(255) NOT NULL,
status SMALLINT DEFAULT 1,
PRIMARY KEY (doi_id),
UNIQUE KEY dois_unique (doi),
KEY dois_context_id (context_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-- Create doi_settings table
CREATE TABLE IF NOT EXISTS doi_settings (
doi_setting_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
doi_id BIGINT NOT NULL,
locale VARCHAR(28) NOT NULL DEFAULT '',
setting_name VARCHAR(255) NOT NULL,
setting_value MEDIUMTEXT,
PRIMARY KEY (doi_setting_id),
UNIQUE KEY doi_settings_unique (doi_id, locale, setting_name),
KEY doi_settings_doi_id (doi_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
Step 2: Add Missing Column to Publications
-- Add doi_id foreign key column
ALTER TABLE publications
ADD COLUMN doi_id BIGINT(20) NULL;
-- Add index for performance
ALTER TABLE publications
ADD INDEX publications_doi_id (doi_id);
Step 3: Migrate Legacy DOI Data
-- Migrate DOIs from publication_settings to dois table
INSERT INTO dois (context_id, doi, status)
SELECT
1 as context_id,
ps.setting_value as doi,
1 as status
FROM publication_settings ps
WHERE ps.setting_name = 'pub-id::doi'
AND ps.setting_value IS NOT NULL
AND ps.setting_value != ''
AND NOT EXISTS (
SELECT 1 FROM dois d
WHERE d.doi = ps.setting_value
)
GROUP BY ps.setting_value;
-- Result: 113 rows inserted
Step 4: Link DOIs to Publications
-- Create publication_id links in doi_settings
INSERT INTO doi_settings (doi_id, locale, setting_name, setting_value)
SELECT
d.doi_id,
'' as locale,
'publication_id' as setting_name,
ps.publication_id as setting_value
FROM dois d
INNER JOIN publication_settings ps ON ps.setting_value = d.doi
WHERE ps.setting_name = 'pub-id::doi'
AND NOT EXISTS (
SELECT 1 FROM doi_settings ds
WHERE ds.doi_id = d.doi_id
AND ds.setting_name = 'publication_id'
);
-- Result: 116 rows inserted
Step 5: Add Context Path
-- Add journal context path for each DOI
INSERT INTO doi_settings (doi_id, locale, setting_name, setting_value)
SELECT
d.doi_id,
'' as locale,
'contextPath' as setting_name,
'index_bjetss' as setting_value
FROM dois d
WHERE NOT EXISTS (
SELECT 1 FROM doi_settings ds
WHERE ds.doi_id = d.doi_id
AND ds.setting_name = 'contextPath'
);
-- Result: 114 rows inserted
Step 6: Populate publications.doi_id
-- Link DOIs back to publications table
UPDATE publications p
INNER JOIN doi_settings ds ON ds.setting_value = p.publication_id
AND ds.setting_name = 'publication_id'
SET p.doi_id = ds.doi_id;
-- Result: 113 publications updated
Step 7: Clear Application Cache
-rm -rf cache/*
```
**Result:** DOI management page now loads successfully, showing all 113 migrated DOIs.
---
## The Frontend UX Disaster
Even after fixing the database, the **user experience was terrible**.
### Problem #1: Cryptic Error Messages
**What users see:**
```
Some DOI(s) could not be updated
We also like to point out that frontend error messages are wrong. They are correctly implemented on the endpoint but the end user does not understand what is the real problem because endpoint response was never shown to the user. Experienced users may use console to debug the problem. This is not fixed in the newest version 3.5 demo on PKP website.
What’s actually happening:
Scenario A: Invalid format
// User enters: https://doi.org/10.31410/Balkans.JETSS.2025.8.2.123-137
// API returns:
{
"doi": ["This is not formatted correctly."]
}
What the error SHOULD say:
“Please enter only the DOI identifier (e.g., 10.31410/Balkans.JETSS), not the full URL. Remove the ‘https://doi.org/‘ prefix.”
Scenario B: Duplicate DOI
// User enters: 10.31410/Balkans.JETSS.2025.8.2.114-1
// API returns:
{
"doi": ["The given DOI suffix is already in use for another published item. Please enter a unique DOI suffix for each item."]
}
But the UI shows: “Some DOI(s) could not be updated”
No indication of:
- ❌ Which article already has this DOI
- ❌ Where to find the conflicting DOI
- ❌ How to resolve the conflict
- Problem #2: Invisible Error Messages
The real errors only appear in browser DevTools:
- Open browser DevTools (F12)
- Go to Network tab
- Click “Save” on DOI form
- Click the failed request (red, status 400)
- View Response tab
There you’ll find the ACTUAL error message.
The Root Cause: Lazy Frontend Development
Looking at the OJS source code:
Backend (API) – Works Correctly:
// File: lib/pkp/api/v1/dois/PKPDoiHandler.php
$errors = Repo::doi()->validate(null, $params);
if ($errors) {
return $response
->withStatus(400)
->withJsonError($errors); // ✓ Returns detailed errors
}
Frontend (JavaScript) – Broken:
// File: pages/dois/DoisHandler.php (Vue component)
.catch(error => {
// ❌ Shows generic message instead of actual error
this.showError('Some DOI(s) could not be updated');
// Should be:
// this.showError(error.response.data.doi[0]);
});
Proposal for OJS upgradeS
- Parse API error responses properly
- Add format validation before submission
- Show conflicting article when duplicate detected
Final Results
✅ OJS upgraded: 3.3.0.9 → 3.4.0.10 (stable)
✅ DOIs migrated: All DOIs successfully recovered
✅ Database integrity: All tables and relationships validated
✅ DOI management: Fully functional
✅ User training: UDekom team trained
✅ Documentation: Complete technical documentation provided
And last but not the least:
✅ We hope that we helped somebody by publishing the issue we faced and possible solutions.
We reached out to multiple consultants and hosting providers — nobody could solve our DOI problem. Most gave up after seeing the complexity of our database state. The We-Deliver.io team not only fixed our broken upgrade but also recovered DOIs we thought were lost forever. Their technical expertise and persistence saved our journal’s credibility. I highly recommend them for any serious OJS work — Nikolina Vrcelj
Use our WP Crossref DOI automated import plugin
If your organization could benefit from similar workflow automation or IT solutions, feel free to reach out—We Deliver IT specializes in turning tedious processes into streamlined systems that let you focus on what truly matters.
About the Author
Stojan Ivanisevic, PhD is the founder and Solutions Architect of We Deliver IT. He is a seasoned IT professional with over 17 years of experience in roles ranging from software developer to strategic consultant. Stojan has led the development of critical systems for government institutions, international organizations, and innovative companies worldwide. With a strong academic background (holding a PhD in Information Technologies) and an eye for practical solutions, he excels at turning complex requirements into clear, functional outcomes. Stojan is known for delivering projects “on time, on budget, and in quality,” upholding the very promise in his company’s name.
SEO Note: For multilingual readers, this article will also be available in Serbian. To read the article in Serbian click here.
