Self-hosted Grist unable to duplicate document with 404 status and "File can't be found at the requested URL." message


I am self-hosting Grist on a local server with the Docker image and reverse proxy setup using Apache. Authentik is used for multi-user support.

Grist is run with the following command:

sudo docker run \
        -p 9999:9999 \
        --env-file ~/grist/.env \
        --env DEBUG=1 \
        -v ~/grist/persist:/persist \
        -it gristlabs/grist

The following is my environment variables file:


Apache configured as follows:

<VirtualHost gr.internal:443>
        ProxyPreserveHost On

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        SSLEngine On
        SSLProxyEngine On

        SSLProxyVerify none
        SSLProxyCheckPeerName Off

        ProxyRequests Off

        ProxyPass / http://gr.internal:9999/
        ProxyPassReverse / http://gr.internal:9999/

        RewriteEngine on
        RewriteCond %{HTTP:Upgrade} websocket [NC]
        RewriteCond %{HTTP:Connection} upgrade [NC]
        RewriteRule ^/?(.*) "ws://gr.internal:9999/$1" [P,L]

        RequestHeader set X-Forwarded-Proto "https"
        RequestHeader set X-Forwarded-Port "443"

        SSLCACertificateFile "/home/USER/grist/persist/ca.crt"
        SSLCertificateFile "/home/USER/grist/persist/sp.pem"
        SSLCertificateKeyFile "/home/USER/grist/persist/sp.key"
        ServerName gr.internal

The functions that I have tried so far seems to work fine so far except for duplicating documents.

When I try to duplicate document and click Save, I keep getting the “File can’t be found at the requested URL.” message.

The log in the console:

2024-01-11 10:21:18.660 - warn: client error stack=Error: Request to https://gr.internal/o/docs/api/docs failed with status 404: Not Found (File can't be found at the requested URL.)
    at y (https://gr.internal/v/unknown/main.bundle.js:2:497670)
    at D.request (https://gr.internal/v/unknown/main.bundle.js:2:497390)
    at async b.countPendingRequest (https://gr.internal/v/unknown/main.bundle.js:2:496091)
    at async D.requestJson (https://gr.internal/v/unknown/main.bundle.js:2:497441)
    at async b.countPendingRequest (https://gr.internal/v/unknown/main.bundle.js:2:496091)
    at async (https://gr.internal/v/unknown/main.bundle.js:2:276499)
    at async https://gr.internal/v/unknown/main.bundle.js:2:484046, message=Request to https://gr.internal/o/docs/api/docs failed with status 404: Not Found (File can't be found at the requested URL.), status=404, userError=File can't be found at the requested URL., docId=6ETKnnsGa37n1UQdxJadd4, page=https://gr.internal/o/docs/6ETKnnsGa37n/Expenses/p/4, language=en-US, platform=Win32, userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36, org=docs, email=USER@DOMAIN.COM, userId=5, altSessionId=3BgGLoD8UMAGCX5uZgjS1P

The request header:

POST /o/docs/api/docs HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,en-AU;q=0.8,id;q=0.7
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 113
Content-Type: application/json
Cookie: grist_core=s%3Ag-r6XmUZc2yvBcAyqoFbFsoW.3H%2B%2BGRwxVfViH5gQqnIL5wvR3gM5i%2B1fuUioGcsqbdw; grist_core_status=S
DNT: 1
Host: gr.internal
Origin: https://gr.internal
Pragma: no-cache
Referer: https://gr.internal/o/docs/6ETKnnsGa37n/Expenses/p/4
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36
X-Requested-With: XMLHttpRequest
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"

Request payload:

{"sourceDocumentId":"6ETKnnsGa37n1UQdxJadd4","workspaceId":2,"documentName":"Expenses (copy)","asTemplate":false}

Response header:

HTTP/1.1 404 Not Found
Date: Thu, 11 Jan 2024 10:21:18 GMT
Server: Apache/2.4.52 (Ubuntu)
X-Powered-By: Express
Content-Language: en-US
Access-Control-Allow-Methods: GET, PATCH, PUT, POST, DELETE, OPTIONS
Access-Control-Allow-Origin: https://gr.internal
Vary: Origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization, Content-Type, X-Requested-With
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: 53
ETag: W/"35-9eL+0vghkXp2kP64+wuXtLOTxh8"
Keep-Alive: timeout=5, max=98
Connection: Keep-Alive


{"error":"File can't be found at the requested URL."}

Based on the above, I tried using curl as follows, but the response is still the same:

curl -v \
        -H "Authorization: Bearer <API_KEY>" \
        -H "Content-Type: application/json" \
        -d '{"sourceDocumentId":"6ETKnnsGa37n1UQdxJadd4","workspaceId":2,"documentName":"Expenses (copy)","asTemplate":false}' \

I tried checking the API documentation but could not find anything on the endpoint /docs with method POST.

I have updated to the latest Docker image 1.1.10, but still unable to duplicate document. Installed on VM and physical server with the same result. Tried with different configurations of the environment variables to the best of my understanding but to no avail, perhaps I am not looking in the right places. Please help.


I don’t have a ready answer, but I suggest looking at "Insufficient access to document to copy it entirely" for Owner · Issue #775 · gristlabs/grist-core · GitHub.

To quote from there: “Document duplication is a bit unusual in that it relies on an internal call from the Grist server to itself within the container.”

In your case, the error is probably from Grist trying to call to itself to access the document, and using a URL that doesn’t actually work within the container. You should be able to find a line in docker logs – when you try duplicating a document, and search logs for “download” – something like this:

2024-01-13 05:05:19.096 - debug: Auth[GET]: /docs/<DOC_ID>/download customHostSession=, method=GET, host=, path=/docs/<DOC_ID>/download, org=test1

The address (like above) is what’s most likely leading to the 404 error, because Grist is using it to fetch a copy of the doc and failing. (The talking to itself is because in multi-server setups, the doc would come from a different server.) The APP_DOC_INTERNAL_URL variable can be set to tell Grist how to talk to itself from inside the container, perhaps worth trying something like http://localhost:9999 in your case.

Thanks for the suggestion Dmitry. I have tried setting the environment variable as suggested but see no change in outcome.

I found a similar issue that is still open on GitHub gristlabs/grist-core, “error when duplicating document #185”. The duplicate function works after I followed the suggestion to set GRIST_SINGLE_ORG=docs.

However, since I aim to have team sites working and the issue seems to be when org is in the path that duplicate does not work, I tried setting up team sites to be in subdomain so that org can be referred to through the subdomain. I removed GRIST_SINGLE_ORG=docs and set GRIST_ORG_IN_PATH=false. After restarting the server, and entering just the domain https://gr.internal, Grist still redirects me to https://gr.internal/o/docs.

2024-01-15 04:13:38.196 - debug: Auth[GET]: gr.internal / customHostSession=, method=GET, host=gr.internal, path=/, org=, email=USER@DOMAIN, userId=5, altSessionId=3BgGLoD8UMAGCX5uZgjS1P
2024-01-15 04:13:38.205 - debug: Redirecting userId 5 to: https://gr.internal/o/docs/
2024-01-15 04:13:38.215 gr.internal GET / 302 17.484 ms - 98
2024-01-15 04:13:38.219 - debug: Auth[GET]: gr.internal / customHostSession=, method=GET, host=gr.internal, path=/, org=docs, email=USER@DOMAIN, userId=5, altSessionId=3BgGLoD8UMAGCX5uZgjS1P
2024-01-15 04:13:38.230 gr.internal GET /o/docs/ 200 6.780 ms - 5141

The links generated to go to different team sites are still encoded in the path instead of the subdomain. When the org is manually entered in the subdomain, Grist recognizes it and shows the requested team site.

2024-01-15 04:24:32.522 - debug: Auth[GET]: / customHostSession=, method=GET,, path=/, org=grok, email=USER@DOMAIN, userId=5, altSessionId=gnLr1ef5KDg13yT1dxxAHv
2024-01-15 04:24:32.552 GET / 200 29.655 ms - 5146

However, links are still generated with org in path which still cause issue since the application does not seem to follow the env GRIST_ORG_IN_PATH=false and the subdomains are not automatically generated causing the following to happen when trying to follow the link to a different team site:

The only way I can find to turn off the org in path is to set GRIST_SINGLE_ORG. This causes the list of team sites to not appear, but existing team sites where available appear in the homepage as follows:

After setting the env GRIST_SINGLE_ORG, accessing team sites other than the one set is no longer possible, be it through the subdomain or path. So both and https://gr.internal/o/grok do not work. And the link generated for the button to other team site in the above image does not look right. Instead of, it showed https://grokgr.internal.

What I can see from the different configurations tested, the env GRIST_ORG_IN_PATH as tested in version 1.1.10 does not seem to affect the behaviour of the application.

How to setup the env so that the application will no longer have org in path but appear in subdomain?
How to setup so that team sites can still function with duplicate function?

Hello Grist,

I do have exactly the same problem, with the latest dockerised version (1.1.13).

My test :

Then, I get the same “File can’t be found at the requested URL.” error when trying to :

  • import a grist file from my local disk
  • duplicate an existing document

It works with GRIST_SINGLE_ORG set, or if I remove the APP_HOME_URL (which is not necessary in a localhost setup…but my real use case needs it).

Does the work to solve this issue will fix this ?

@paul-grist, do you know any workaround to allow us to use the duplicate functionnality in a self hosted setup which is not single org ?

We really need this functionnality to switch our data submission process to grist.

Thank you,


Hi @Xavier_de_Rochefort, there’s been movement recently related to duplicating documents. Recently @Jordi_Gutierrez_Herm made a fix:

Your test case, with an explicitly set APP_HOME_URL that includes localhost, still fails for me on latest Grist, I’m not entirely sure what I’m seeing there.

The APP_HOME_INTERNAL_URL change in the linked pull request could help, more generally.

Thank you for your answer @paul-grist

I see that the fix may be soon deployed as it has been with “preview” tag for 2 days.

I stay tuned and cross my fingers.


Hello, @Xavier_de_Rochefort.

After some debugging, I can see that at the latest docker image, the problem can be resolved by also setting the environment variable APP_DOC_INTERNAL_URL, for example, --env APP_DOC_INTERNAL_URL=

Allow me to give a more technical description.

The reason APP_DOC_INTERNAL_URL is needed is because otherwise an internal call to http://localhost:8585/o/docs/api/docs fails to identify a call from here as being internal. When this call isn’t recognised as internal, the code tries to look for a custom org, which doesn’t exist. This ends up bubbling as an obscure 404 error to the end user.

I am not sure yet if we should remedy this by documenting the need to also set APP_DOC_INTERNAL_URL or if there is a cleaner method to automatically detect internal calls and remove the need to set this extra environment variable.

1 Like

Hello @Jordi_Gutierrez_Her1,

Thanks a lot. I have just tested, and it works now !


1 Like