I've looked through the source code for you but I'm drawing a blank. I thought there was an arbitrery timeout value in the token however in 2.9.2 this seems to have been depreciated (or I imagined it)!
I'm also going to assume the error is not a missing XID/CSRF_token as this has been covered above and no answer is forth-coming.
First Question : How are you creating your form? If you could provide your code that would be great. Is it a Freeform, a Channel Entries form or your own custom one??
Anyway, to help you debug, when you create your form, if you use EE tags or the php
ee()->functions->form_declaration() a CSRF token should be generated (it's actually generated super early in the request but for simple example), this will be added to the hidden fields of your form that
form_declaration/tags print out to the template. In the back what happens is that this token is added to the
exp_security_hashes table so it can be referenced. Assuming you're using Cookie based (as opposed to Session ID based).
Debug point 1 : When you create/display your form is the correct entry being added to the DB table?
Question 2 : Are you using standard Cookie based sessions? You can see this in the CP under
Admin > Security and Privacy > Security and Sessions > Website Session Type.
So, now we've debugged the token setting, lets look at the submission. One clue from your OP is
I have a [..] form that, if used after a certain time, will throw a [...] error.
This is what made me skim through to see if there is an expiry. There is not (from my scan, I'm not infallable!). The first thing I note is that if you are using cookie based authentication there is a hard coded expiry on the token! In file
expressionengine/libraries/csrf/Cookie.php line 42 you'll see this :
public function get_expiration()
return 60 * 60 * 2; // 2 hours
That's a 2 hour expiry on a cookie based session right there, this could cause invalid CSRF's.
Debug Point 2 : If you are using Session ID based security on the front end then are you ensuring that the
S parameter is passed along with the form submission, either as a cookie or in the URL string? If you submit a form without an
S parameter it can't match the users session and can't check the token.
So finally, now we assume your using session ID based authentication. we can see from the file
expressionengine/libraries/csrf/Database.php line 35 :
public function get_expiration()
return 0; // never - times out with session
This means the token is valid as long as the users session is valid. So we now have to check the session settings... Now, the session handling was changed in 2.8.0, however we can get some clues from the file
system/expressionengine/libraries/Session.php (This file is well worth reading just becuase it explains the session types at the top in comments!!), on lines 73 and 74:
public $user_session_len = 7200; // User sessions expire in two hours
public $cpan_session_len = 3600; // Admin sessions expire in one hour
That's a 2 hour timeout on users using the front end in Sesion ID mode. It's also worth checking your cookie domain in both cases, and if you're posting from http to https or vice versa.
Question 3: How long in terms of inactivity does the form take to expire? 2 hours??
Debug Point 3: Try altering the cookie/session timeout values on your local site, see if it holds the form 'open' for longer/shorter. If you find that this is what is causing your timeouts, there's a load of modules (or you can core hackz it yourself) out there for you to better control the session.
I do hope the above helps you out a bit. Of course if this (and none of the other answers) does not solve it for you then all this typings been for nothing! (hehe) but please do come back to us (via comments, or edit your OP) with your further findings.
If one of the answers did help you solve it please do mark the answer, alternatively if you get to the bottom of it please "Answer your own Question" so we can all bask in the glorious solution!
XIDis deprecated, but has not been removed, so it should still work in 2.9.2. Is there a proxy in front of the web server? Have you viewed the rendered source to see what is being put into your input?