Collection record displays incorrectly and cannot be edited/deleted

55 views
Skip to first unread message

Francisco Bosch Puche

unread,
Jun 22, 2026, 1:22:36 PM (9 days ago) Jun 22
to AtoM Users

Dear colleagues,

Hello from the Griffith Institute Archive, University of Oxford. 

We have encountered an issue with a specific collection record in our catalogue and would appreciate any advice on how to diagnose or resolve it.

The collection in question is:

https://archive.griffith.ox.ac.uk/index.php/clackson-collection

Unlike all other collections in the catalogue, this record does not display correctly. When logged in, it is possible to make some edits, but it is not possible to update the slug, delete the record, or move it to draft status. We have only recently noticed this issue, so unfortunately we do not know how long the record has been displaying in this way.

The collection has many descendant records, which can be viewed here:

https://archive.griffith.ox.ac.uk/index.php/clackson-1

From this page, the full hierarchy is visible. However, it is not possible to navigate back to the parent collection record from the tree view, nor does the collection record itself provide access to the hierarchy.

This is the only collection in the catalogue (out of approximately 170 collections) that behaves in this way.

We have considered creating a new collection and moving all descendant records to it. However, because the current collection cannot be deleted or unpublished, users would ultimately see two collections, which is not an ideal solution.

We are currently running AtoM version 2.10.1 (build 197).

Has anyone encountered a similar issue, or can you suggest how we might investigate and fix this record?

Many thanks in advance.

Best wishes,

Francisco Bosch-Puche
GI Archive Curator

P.S. This is my first time posting to the group, as a former colleague would usually have handled these matters. Please forgive me if I have not followed the usual procedure or if my description of the issue is unclear.

Alberto Pereira

unread,
Jun 22, 2026, 1:39:26 PM (9 days ago) Jun 22
to ica-ato...@googlegroups.com
Hi Francisco,

This specific page (https://archive.griffith.ox.ac.uk/index.php/clackson-collection) is throwing an internal error (500). It's possible to view that it is happening in the browser, but not what the error is exactly.
To know exactly what's going on, you'll need to check with the IT admin who has access to the server to verify the logs. From there, you'll have more information with which to work. I suspect a broken hierarchy somewhere, and maybe rebuilding the nested set is enough (https://www.accesstomemory.org/en/docs/2.10/admin-manual/maintenance/cli-tools/#rebuild-the-nested-set), but I can't be sure.

good luck!

--
You received this message because you are subscribed to the Google Groups "AtoM Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ica-atom-user...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/ica-atom-users/1bfc177c-f58e-4054-b65c-668e04aaf96cn%40googlegroups.com.

Clara Rosales

unread,
Jun 22, 2026, 1:52:17 PM (9 days ago) Jun 22
to ica-ato...@googlegroups.com
Hi Francisco,
I think the problem stems from the "Rights" field. Run this query on your database:
SELECT s2.slug FROM relation r INNER JOIN slug s1 ON r.subject_id = s1.object_id INNER JOIN slug s2 ON r.object_id = s2.object_id WHERE s1.slug = 'clackson-collection' AND r.type_id = 168;
It will return a string of numbers and letters, like a1b2-3cc3-ddd8.
Delete the "Rights" field associated with the 'clackson-collection' description at: https://archive.griffith.ox.ac.uk/index.php/a1b2-3cc3-ddd8/right/delete If the error lies there, the record will display correctly after deletion.
Greetings!


--

Francisco Bosch Puche

unread,
Jun 23, 2026, 6:16:19 AM (8 days ago) Jun 23
to AtoM Users

Thank you both, Alberto and Clara, for your quick replies. 

I have forwarded them to our IT officer. Let's hope he manages to find a solution based on your suggestions. I will report back.

Thanks again.

Cisco

Johan Pieterse

unread,
Jun 23, 2026, 6:53:51 AM (8 days ago) Jun 23
to AtoM Users
Hi Francisco,

  Adding to Alberto's and Clara's replies, both are pointing you in good directions, and I can confirm one detail. I checked against the AtoM 2.10 base: the type_id = 168 in Clara's query is QubitTerm::RIGHT_ID, i.e.
  the rights relation. So her query is correctly pulling the rights record(s) attached to that collection, and a broken rights record is the most likely culprit for exactly this behaviour.

  Here's why the symptom pattern fits: displaying the record, updating its slug, deleting it, and moving it to draft all load (and re-save/re-index) the record's full related-object graph, whereas the edit form only
  loads a narrower slice, which is why partial editing still works while everything else 500s. A single dangling related row (e.g. a rights object whose detail row or rights-holder was removed, leaving a relation that
  points at nothing the template can render) will fatal every full-graph operation, and would affect only this one record out of your ~170.

A couple of suggestions before deleting anything:

1. Get the actual error first. Alberto's right, the stack trace (web-server/php-fpm error log, or set debug in config/settings.yml) names the exact failing class and the offending object, which removes the guesswork.
2. Confirm the broken relation with a non-destructive query. This lists every relation on the record and flags any whose target object no longer exists:

SELECT r.id AS relation_id, r.type_id, r.object_id,
       CASE WHEN o.id IS NULL THEN '*** MISSING TARGET ***' ELSE 'ok' END AS target
FROM slug s
JOIN relation r   ON r.subject_id = s.object_id
LEFT JOIN object o ON o.id = r.object_id
WHERE s.slug = 'clackson-collection';
 
3. And specifically for a rights row that's referenced but gone:

SELECT r.id, r.object_id
FROM slug s
JOIN relation r ON r.subject_id = s.object_id AND r.type_id = 168
LEFT JOIN rights rt ON rt.id = r.object_id
WHERE s.slug = 'clackson-collection' AND rt.id IS NULL;

4. Back up, then remove the specific offending row(s). One note: the /right/delete link Clara mentioned runs through the same code path that's 500ing, so it may error too, removing the identified relation/rights row directly in SQL is often more reliable. Afterwards, reindex that record (php symfony search:populate, or a per-record reindex) so search/browse reflect it. I'd only rebuild the nested set if the stack trace actually implicates it — since your descendants and tree render fine, the nested set is probably intact.

You shouldn't need the "new collection + move 170 children" workaround if it's a single dangling relation, which this looks like.

Hope that helps, good luck, and welcome to the list.
 
Best wishes,
Johan Pieterse
The Archive and Heritage Group (Pty) Ltd
https://heratio.theahg.co.za

Francisco Bosch Puche

unread,
Jun 23, 2026, 7:09:36 PM (7 days ago) Jun 23
to AtoM Users
Perfect, thanks Johan. I've forwarded your email to our IT - let's see if they can fix the problem following all your suggestions.
All best,
Cisco

carpo...@gmail.com

unread,
Jun 24, 2026, 11:28:16 AM (7 days ago) Jun 24
to AtoM Users
Dear all,

Error message specifically is: 

2026/06/24 15:23:48 [error] 144205#144205: *137925 FastCGI sent in stderr: "PHP message: PHP Fatal error:  Uncaught TypeError: get_class(): Argument #1 ($object) must be of type object, null given in /usr/share/nginx/atom/plugins/qbAclPlugin/lib/QubitAcl.class.php:770
Stack trace:
#0 /usr/share/nginx/atom/plugins/qbAclPlugin/lib/QubitAcl.class.php(132): QubitAcl::checkAccessByClass()
#1 /usr/share/nginx/atom/apps/qubit/modules/right/templates/_right.php(4): QubitAcl::check()
#2 /usr/share/nginx/atom/cache/qubit/prod/config/config_core_compile.yml.php(3954): require('...')
#3 /usr/share/nginx/atom/vendor/symfony/lib/view/sfPartialView.class.php(124): sfPHPView->renderFile()
#4 /usr/share/nginx/atom/vendor/symfony/lib/helper/PartialHelper.php(220): sfPartialView->render()
#5 /usr/share/nginx/atom/apps/qubit/modules/digitalobject/templates/_rights.php(19): get_partial()
#6 /usr/share/nginx/atom/cache/qubit/prod/config/config_core_compile.yml.php(3954): require('...')
#7 /usr/share/nginx/atom/vendor/symfony/lib/view/sfPartialView.class.php(124): sfPHPView->renderFile()
#8 /us" while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "GET /index.php/clackson-collection HTTP/1.1", upstream: "fastcgi://unix:/run/php-fpm.atom.sock:", host: "archive.griffith.ox.ac.uk"

I've had a look into this particular issue, and this seems to be related to an credit in the rights field of that particular record, the only record in the database which has one: 

mysql> SELECT io.id, s.slug, do.id as do_id, r.id as rel_id
    -> FROM information_object io
    -> JOIN digital_object do ON do.object_id = io.id
    -> JOIN relation r ON r.subject_id = do.id AND r.type_id = 168
    -> JOIN slug s ON s.object_id = io.id
    -> LIMIT 10;

+------+---------------------+-------+--------+
| id   | slug                | do_id | rel_id |
+------+---------------------+-------+--------+
| 9772 | clackson-collection | 20084 |  20091 |
+------+---------------------+-------+--------+

and a much simpler SQL query of: 

mysql> select * from rights;
+-------+------------+----------+----------+------------------+---------------------+-----------------------+------------------------+----------------------------+---------------------+----------------+
| id    | start_date | end_date | basis_id | rights_holder_id | copyright_status_id | copyright_status_date | copyright_jurisdiction | statute_determination_date | statute_citation_id | source_culture |
+-------+------------+----------+----------+------------------+---------------------+-----------------------+------------------------+----------------------------+---------------------+----------------+
| 20090 | 2017-04-28 | NULL     |      170 |             NULL |                 336 | NULL                  | NULL                   | NULL                       |                NULL | en             |
+-------+------------+----------+----------+------------------+---------------------+-----------------------+------------------------+----------------------------+---------------------+----------------+
1 row in set (0.00 sec)

What is this table? Is this a legacy hangover (we've been running AtoM since 2016 and this wouldn't be the first time a ghost of the past has cause us trouble....

Many Thanks
Richard Carpenter
Technical Lead, Humanities IT, University of Oxford

pieters...@gmail.com

unread,
Jun 24, 2026, 11:58:38 AM (7 days ago) Jun 24
to ica-ato...@googlegroups.com

Hi Richard,

 

That stack trace is the smoking gun, and it confirms Clara was on exactly the right track, it is the rights statement, and I can now tell you why it probably breaks and why it's only this one record.

 

First, your direct question: the rights table is not a legacy hangover. It's the standard, current AtoM/PREMIS rights store. Every time someone adds an entry in the "Rights area" of a description or of a digital object, AtoM writes one row here and links it through the relation table with type_id = 168 (QubitTerm::RIGHT_ID). Your single row (id 20090,basis_id 170 = a "policy"/credit-type basis, rights_holder_id NULL) is a perfectly valid rights statement. The reason it's the only one in your catalogue that misbehaves is what your first query revealed: this rights statement is attached to the digital object, not to the description. Note your join — relation r ON r.subject_id = do.id — the relation's subject is digital_object 20084, not information_object 9772. That's the rare case that trips the bug.

 

Why it 500s. Walk the trace from the bottom up:

- digitalobject/templates/_rights.php:19 renders the rights partial for a digital-object-level right with get_partial('right/right', ['resource' => $item->object, 'object' =>  $item]).

- right/templates/_right.php:4 then calls QubitAcl::check($relatedObject, 'update').

- But that digital-object caller never passes $relatedObject (the information-object path, right/_relatedRights.php, does, it sets 'relatedObject' => $resource; the digital-object

  path doesn't). So $relatedObject is null.

- QubitAcl::check(null,…) → checkAccessByClass(null,…) → switch (get_class($resource)) at QubitAcl.class.php:775 → get_class(null), which is a hard TypeError on PHP 8.

 

So, this isn't data corruption, a dangling relation, or a broken nested set, your rights row and its relation are intact, and your hierarchy is fine (which is why the descendants and tree render). It's a base-AtoM template that forgets to hand the ACL check its resource, and it only bites when a rights statement lives at the digital-object level. It also explains your exact symptom pattern: viewing/slug-change/delete/draft all render the full record (which renders this partial and crashes), whereas the edit form loads a narrower slice that doesn't hit it.

 

The fix should be to remove that one digital-object rights statement. The good news: the right module's delete action resolves the related object correctly (it reads it from the relation's subject), so unlike the page view it will not crash. You just can't see the Edit/Delete links because the view dies first, so go to the delete URL directly using the rights record's own slug:

- 1. Find the slug of the rights record (object_id = the rights row id, 20090)

- 1. Find the slug of the rights record (object_id = the rights row id, 20090)

SELECT slug FROM slug WHERE object_id = 20090;

 

  Then (logged in as an administrator) visit:

https://archive.griffith.ox.ac.uk/index.php/<that-slug>/right/delete

 

Confirm the deletion. That routes through AtoM's own cascade (rights row, its i18n, any granted-rights, and the relation), so it's clean. After it's gone, the clackson-collection page will render normally.

 

If your team would rather do it in SQL (back up first), identify the full footprint before deleting anything:

 

-- The rights row, its relation, i18n and any granted rights

SELECT * FROM relation     WHERE id = 20091;          -- the link (subject=DO 20084, object=rights 20090)

SELECT * FROM rights       WHERE id = 20090;

SELECT * FROM rights_i18n  WHERE id = 20090;

SELECT * FROM granted_right WHERE rights_id = 20090;

SELECT * FROM slug         WHERE object_id = 20090;

 

I'd strongly prefer the through-the-app delete above to a hand-written SQL cascade, it guarantees nothing is left dangling. Either way, finish with a reindex so search/browse pick up the change:

 

# per-record is enough here; full populate also fine

sudo -u www-data php symfony search:populate

 

You do not need the "new collection + move 170 children" workaround, it's a single rights statement.

 

Hope that gets you sorted.

 

Regards

Johan Pieterse

The Archive and Heritage Digital Commons Group (Pty) Ltd

https://theahg.co.za

+27 082 337-1406

carpo...@gmail.com

unread,
Jun 24, 2026, 4:51:35 PM (7 days ago) Jun 24
to AtoM Users
Dear Johan, 

Agreed - i'm of the same opinion that SQL deletion statements are rather best avoided in any CMS! 

Fix applied, problem resolved. Many thanks for your assistance in this matter - if you ever visit us in Oxford - please do get in touch to say hi!

Many Thanks
Richard Carpenter
Technical Lead, Humanities IT, University of Oxford 

pieters...@gmail.com

unread,
Jun 25, 2026, 1:04:01 AM (6 days ago) Jun 25
to ica-ato...@googlegroups.com

Hi Richard

 

Glad to hear.

 

I will surely take up your offer.

 

Groete / Regards

Johan Pieterse

Reply all
Reply to author
Forward
0 new messages