How to Fix OJS HTTP 500 Error: Duplicate Entry for sessions_pkey

When your OJS journal suddenly throws an HTTP 500 error and the Apache error log shows Duplicate entry for key 'sessions_pkey', the entire site — homepage, submissions, admin dashboard — can go offline. This error has been reported on OJS versions 3.3.0 through 3.4.0, and in some cases even after following the standard fixes, the problem persists.

What follows is based on a real troubleshooting case from the PKP Community Forum, where a journal administrator at eco.mdp.edu.ar spent over three weeks debugging this error before finding a working resolution. The case reveals that what looks like a simple database duplicate-key problem can actually point to a much deeper configuration conflict — and the solution may not be what you expect.

Understanding the Error

A typical error entry looks like this:

PHP Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'abc123...' for key 'sessions_pkey'

The sessions_pkey is the primary key constraint on the session_id column in OJS’s database-backed sessions table. When OJS calls SessionManager::createSession() and attempts an INSERT with a session ID that already exists in the table, MySQL or PostgreSQL rejects the insert and OJS throws a fatal PDOException.

The trigger can vary: a database migration or restore that left stale rows, an auto-increment sequence that drifted out of sync, or — as the case study in this guide showed — a deeper conflict involving PHP’s session handler registration and server-level configuration.

Step 1: Back Up Your Database First

Do not run any SQL commands without a confirmed backup. This is non-negotiable.

For MySQL / MariaDB:

mysqldump -u [username] -p [database_name] > ojs_backup_$(date +%Y%m%d).sql

For PostgreSQL:

pg_dump -U [username] [database_name] > ojs_backup_$(date +%Y%m%d).sql

Confirm the backup file exists on disk before moving to the next step.

Step 2: Truncate the Sessions Table (With a Caveat)

Emptying the sessions table is a logical first move — sessions are transient, and users will receive new session IDs on their next visit. No articles, submissions, or user accounts are affected.

For MySQL / MariaDB:

TRUNCATE TABLE sessions;

For PostgreSQL:

TRUNCATE TABLE sessions RESTART IDENTITY;

The RESTART IDENTITY clause resets the sequence backing the primary key column, which is particularly relevant if the duplicate-key problem stems from a sequence mismatch.

Important: In the real-world case behind this guide, the administrator truncated the sessions table and the error returned immediately on the next page load. If you see the same behavior — the table is empty but the error fires again on the very next request — stop here. The problem is not stale session data; it is at the session handler or PHP configuration level, and repeatedly truncating the table will not help. Proceed to the deeper diagnostics below.

Step 3: Clear OPcache and OJS Application Cache

PHP’s OPcache may serve a broken state even after the database is clean. Clear it by restarting PHP-FPM:

sudo systemctl restart php8.2-fpm

Then remove OJS’s own cache directory:

rm -rf /path/to/ojs/cache/*

If the error clears, you are likely in good shape. If it returns immediately — especially with the same duplicate session ID appearing across multiple requests — the root cause is deeper.

Step 4: Verify the session.auto_start PHP Setting

OJS uses a custom session handler (SessionManager implements SessionHandlerInterface) that stores all session data in the database. For this to work, PHP must not auto-start a session before OJS registers its handler.

Check the current value:

php -i | grep session.auto_start

The expected output:

session.auto_start = Off => Off

If you see On or 1, PHP is starting sessions before OJS takes over, which means sessions are written to the filesystem instead of the database — or worse, they collide with OJS’s own database-backed session management. Fix it by editing php.ini:

session.auto_start = 0

Then restart PHP-FPM. This is especially important on shared hosting or servers that run multiple PHP applications alongside OJS.

Step 5: Check for Conflicting Cache-Control Headers

During the PKP Forum debugging of this error, a PKP developer discovered a revealing configuration problem by inspecting the HTTP response headers of the affected site. The server was emitting two conflicting cache-control values:

cache-control: no-store           (set by OJS)
cache-control: max-age=2592000    (set by Apache mod_expires)

OJS correctly set no-store (do not cache dynamic pages), but Apache’s mod_expires or mod_headers was appending max-age=2592000 — a 30-day cache directive — to the same response. The max-age value exactly matches OJS’s default session_lifetime of 30 days, which is not a coincidence. When pages are cached with stale session references, OJS’s session handler can encounter collisions on the next request.

To check for this problem:

  1. Open your journal in a browser, open the developer tools (F12), go to the Network tab, reload the page, and inspect the response headers for duplicate cache-control entries.
    developer tools
  2. On the server, check your Apache configuration for mod_expires directives:
grep -r "ExpiresActive\|ExpiresDefault\|mod_expires" /etc/apache2/
grep -r "mod_headers" /etc/apache2/
  1. Look for lines like:
ExpiresActive On
ExpiresDefault "access plus 30 days"

Or in .htaccess:

Header set Cache-Control "max-age=2592000"

If you find these, add a block to exclude PHP files from the cache policy:

<FilesMatch "\.php$">
    ExpiresActive Off
    Header set Cache-Control "no-store, no-cache, must-revalidate"
</FilesMatch>

In the forum case, this fix alone did not fully resolve the issue — the underlying session handler problem remained — but it is an important server-level diagnostic that can prevent future cache-related session conflicts.

Step 6: When Multiple PHP Applications Share a Server

If your server runs more than one PHP application alongside OJS — for example, Moodle at /cv/ and OJS at /revistas/ on the same Apache or Nginx instance — and both applications show session errors around the same time, the problem is almost certainly at the server or PHP configuration level.

In the case behind this guide, the same server hosted Moodle and OJS. The debug logs revealed telling patterns:

  • Every request triggered SESSION INIT: userSession=NULL — OJS had no session record for any incoming request.
  • Every request triggered SESSION CREATE: id=... already exists — adopted, user_id=NULL — the handler found a collision, adopted the existing row, but the user remained unauthenticated.
  • Zero SESSION WRITE calls appeared in the logs — the session handler’s write() method was never invoked, meaning session data was never persisted to the database between requests.

These three symptoms together point to a PHP-level session handler conflict: OJS’s SessionManager handler was either not being registered in time, or a shared PHP-FPM pool was interfering with the custom handler lifecycle. The administrator confirmed that session.auto_start was correctly set to Off, ruling out the most obvious configuration issue.

Actionable steps if you see similar symptoms:

  • Isolate OJS in its own PHP-FPM pool with a dedicated php.ini or pool configuration file, rather than sharing a pool with other applications.
  • If you are on shared hosting, contact your provider and ask them to review the PHP session handler configuration for your account — specifically whether any server-wide session directives could interfere with application-level session handlers.
  • Check whether any .htaccess or Apache virtual host directives set php_value session.* globally, which can override OJS’s own session configuration.

Step 7: When Nothing Else Works — The Upgrade Path

This section corrects and expands the original draft based on the full forum timeline. Here is exactly what happened.

Sequence of events:

  • Initial version: OJS 3.4.0-5 (PHP 8.2). The sessions_pkey error began on a Sunday morning with no prior warning and no known server changes.
  • First attempt — truncate sessions: Did not resolve. Error returned immediately.
  • Code patches applied: Two file edits to SessionDAO.php and SessionManager.php were suggested by PKP support. The patches stopped the HTTP 500 but created a login loop — users could not log in. Reverting the patches brought back the HTTP 500.
  • Upgrade to 3.4.0-10: PKP’s official fix for this class of error was introduced in PR #12081 and included starting in OJS 3.4.0-8. The administrator upgraded to 3.4.0-10, expecting the fix to resolve the problem. It did not. The sessions_pkey error returned unchanged on the first request. Additionally, the bundled Web Feed plugin was incompatible with 3.4.0-10 (a packaging error later acknowledged by PKP) and had to be disabled.
  • Apache Cache-Control fix applied: The mod_expires conflict was corrected. The issue persisted.
  • Debug logging patch applied: PKP support added extensive debug logging to SessionManager. The logs revealed the userSession=NULL and zero SESSION WRITE pattern described in Step 6.
  • Final upgrade to 3.5.0-4: After exhausting all other options, the administrator upgraded to OJS 3.5.0-4. The error was resolved immediately. The platform came back online, and all journals resumed normal editorial operations.

Key takeaways:

  • The PKP fix (PR #12081, released in 3.4.0-8) targets the insertObject / updateObject race condition in SessionDAO. It resolved the session duplicate entry for most users who applied it. In this specific case, however, the fix did not address the root cause — which was likely a server-level PHP session handler conflict, not an OJS code bug.
  • The upgrade to 3.5.0-4 worked, but the exact root cause was never identified, as confirmed by the administrator in their final forum post. The upgrade was a practical workaround, not a targeted fix.
  • Upgrading OJS requires planning, a staging test, and a rollback strategy — but when a journal has been offline for weeks and editorial teams need access, it becomes the responsible path forward.

Before upgrading, make sure:

  • You have a full database and files backup
  • Your PHP version meets the target OJS version requirements
  • All plugins and third-party themes are compatible with the new version
  • The PKP Preservation Network plugin (if used) is compatible with the target version — the forum administrator flagged this as a concern before upgrading to 3.5

How Open Journal Theme Can Help

If you manage an OJS installation and run into database or session errors like this one, having a professionally configured setup makes recovery faster and less stressful. A well-maintained theme and plugin stack reduces the risk of silent configuration issues going unnoticed until they take your site offline.

Open Journal Theme provides professional OJS themes tested across multiple OJS versions, alongside OJS maintenance services that cover server configuration auditing, database optimization, and version upgrade planning. If your journal shares a server with other applications or runs on a self-managed VPS, a configuration audit can catch session handler conflicts and server-level misconfigurations before they escalate into downtime.

For journals that want a stable foundation across OJS upgrades, the Noble theme is built for long-term reliability and includes documentation covering common troubleshooting scenarios — including the session and configuration issues described in this guide.

Quick Reference: Commands Summary

ActionMySQL / MariaDBPostgreSQL
Backupmysqldump -u user -p db > backup.sqlpg_dump -U user db > backup.sql
Clear sessionsTRUNCATE TABLE sessions;TRUNCATE TABLE sessions RESTART IDENTITY;
Check auto_start`php -i \grep session.auto_start`
Check mod_expires`grep -r “ExpiresActive\ExpiresDefault” /etc/apache2/`
Restart PHPsudo systemctl restart php8.2-fpmSame

Attribution: This guide is based on a real troubleshooting thread on the PKP Community Forum (thread #98150, April 14 – May 6, 2026). The case involved a journal administrator on OJS 3.4.0-5 / PHP 8.2, with their site hosted on a server also running Moodle. After three weeks of debugging — including table truncation, OPcache clearing, PKP code patches, Apache configuration changes, and two version upgrades — the site was restored by upgrading to OJS 3.5.0-4. The issue was operationally resolved, though the exact root cause at the server/PHP session handler level was not conclusively identified.

About the Author
user-avatar

Hello! I'm Ghazi, im OJS Technical Support from Openjournaltheme. Have a passion for linux, helping solve publisher problems related to the use of OJS, OMP and Eprints.

Leave a Comment

Your email address will not be published. Required fields are marked *

Open Journal Theme

Need More Services  or Question?

Openjournaltheme.com started in 2016 by a passionate team that focused to provide affordable OJS, OMP,  OPS,  Dspace, Eprints products and services. Our mission to help publishers to be more focus on their content research rather than tackled by many technical OJS issues.

Under the legal company name :
Inovasi Informatik Sinergi Inc.

Secure Payment :

All the client’s financial account data is stored in the respective third-party site (such as Paypal, Wise and Direct Payment).
*Payment on Credit card can be done by request
Your financial account is guaranteed protection. We never keep any of the clients’ financial data.

Index