Shipmunks

Sitemap could not be read: what to check first

The details page says the sitemap could not be read when the fetch failed. Here is how to tell that from a file Google already fetched, using a server run on 24 Oct 2026.

Decision tree for Couldn't fetch and Sitemap could not be read. Request the exact URL. Stop for a 404, a 500, a redirect, an HTML body, or a robots.txt block. Keep going when the response is 200, a parser accepts the XML, and robots.txt allows the URL. A new sitemap can still show the error before Google processes it, and that screen was not reproduced here. If the report says the sitemap had errors, fix the XML or encoding, gzip, a file over 50,000 URLs or 50MB, or URLs on the wrong host, protocol, or folder.
On this page
  1. The short answer
  2. Before you start
  3. What the two lines mean
  4. Work down this tree
  5. Fix the cause you have
  6. What we tested
  7. When the file is already fine
  8. FAQ
  9. Sources
  10. Related

The short answer

Sitemap could not be read means Google did not fetch the file. Couldn’t fetch is the same failure on the main Sitemaps report. Check the status code, the body, robots.txt, and redirects before you edit the XML. A new sitemap can show both lines while Google has not processed it yet.

Before you start

  • The exact sitemap URL from the Search Console row, not a close variant
  • A terminal, so you can request that URL yourself
  • The property that matches the host. http and https are different properties, and www is a different host

What the two lines mean

The Sitemaps report has three statuses for the last request. Success means the sitemap was fetched and read with no errors. Couldn’t fetch means Google could not fetch the sitemap. Sitemap had errors means Google fetched it, read part of it, and hit errors.

Open the row. If the details page says “Sitemap could not be read,” Google’s help says the fetch failed. If it says the sitemap can be read but has errors, you are in the other bucket, and the page lists the errors.

I could not open that screen. Search Console only shows it inside a verified property, and this test had none. What I could do is serve the usual broken files and record what came back. Where the help page names a status, I cite that page. Where it does not, I say so.

The help page’s own reasons for Couldn’t fetch are: robots.txt blocks the sitemap, the site has a manual action, the URL is a 404, some other error such as the server being down, and low crawl demand. A manual action and low crawl demand are real entries on that list. I did not reproduce either one.

Two sentences on that page sit next to each other and do not say the same thing. One says the sitemap should be fetched immediately. Another says some of these errors are temporary, and that Google retries for a few days. Build and submit a sitemap adds that submitting a sitemap is a hint and does not guarantee Google will download it. The claim that a brand-new sitemap shows Couldn’t fetch, or Sitemap could not be read, simply because it has not been processed yet comes from barryhunter on Google’s community forum (18 Oct 2022), not from the help page. The post says that until the sitemap is processed it can display Couldn’t fetch, or the longer Sitemap could not be read, and that in reality it just means Google has not fetched it yet.

The Type column has an official version of “not yet.” Unknown means the file is not a known sitemap type, or the sitemap has not been processed yet.

What to do with the status

Does not clear it

Rewrite the XML first

Broken XML matters after a fetch. If Google never got the file, a cleaner sitemap does not change the status.

Do this first

Request the exact URL

  • status code
  • body
  • robots.txt
  • redirect

Work down this tree

Fetch failures are the red boxes. The green boxes are a file Google could get. The last four rows are the other status, Sitemap had errors. Same words people type into search. Different repair.

Decision tree for Couldn't fetch and Sitemap could not be read. Request the exact URL. Stop for a 404, a 500, a redirect, an HTML body, or a robots.txt block. Keep going when the response is 200, a parser accepts the XML, and robots.txt allows the URL. A new sitemap can still show the error before Google processes it, and that screen was not reproduced here. If the report says the sitemap had errors, fix the XML or encoding, gzip, a file over 50,000 URLs or 50MB, or URLs on the wrong host, protocol, or folder.
Stop at the first red box. Green means the file came back and you keep going. The Search Console screen itself was not reproduced.

The steps below are that tree, with the responses from the test server.

Fix the cause you have

Nine checks. Stop at the first one that fails.

  1. Request the exact URL you submitted.

    Copy it from the report. Do not fix it up. The help page says the report shows the exact URL you submitted, and that a sitemap for http://example.com does not appear on the https property or the www property.

    On 24 Oct 2026 the good file was one URL, served at http://127.0.0.1:8765/ok.xml.

    ok.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
      <url><loc>http://127.0.0.1:8765/page</loc></url>
    </urlset>
    curl -I http://127.0.0.1:8765/ok.xml
    HTTP/1.1 200 OK
    Content-Type: application/xml; charset=utf-8
    Content-Length: 161
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    xmllint (libxml 2.9.14) exited 0 on the downloaded body. The same body validated against the sitemap schema on sitemaps.org: /tmp/sitemap-lab/body-_ok.xml validates.

    That is the control. Every failure below was a change to this file, or to the way it was served.

  2. Read the status code.

    A 404 is the case the help page names outright: the URL is wrong, or the file is not there. I served /gone.xml as a missing sitemap, and /missing-child.xml as a child of a sitemap index.

    curl -I http://127.0.0.1:8765/gone.xml
    HTTP/1.1 404 Not Found
    Content-Type: text/plain; charset=utf-8
    Content-Length: 10
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    /missing-child.xml returned the same status line. The index that pointed at it did not. /index.xml returned 200, and it validated against the sitemap index schema. The index can be fine while one child is a 404. Fix: make every child URL in the index return 200, or remove the dead row. The help page also lists an “HTTP error” that shows the status code, and says a general HTTP error can be a 404 too. I cannot tell you which of those labels a property would show.

    A dead server is the other fetch failure I could force. /boom.xml returned 500. The help page files that under “some other general error,” with server unavailability as the example.

    curl -I http://127.0.0.1:8765/boom.xml
    HTTP/1.1 500 Internal Server Error
    Content-Type: text/plain; charset=utf-8
    Content-Length: 12
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    Fix for a 404: submit the URL that exists. Fix for a 500: the server has to answer before the sitemap matters. Google retries for a few days, then stops. After the file stays up, resubmit.

  3. Check that the body is a sitemap.

    Two different files, same status code.

    /html-type.xml was the good sitemap, byte for byte, 161 bytes. The only change was the header.

    curl -I http://127.0.0.1:8765/html-type.xml
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    Content-Length: 161
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    xmllint exited 0. The body is a sitemap. The header says it is a web page. Google’s error list does not mention Content-Type. I will not invent a Search Console label for this header. Serve application/xml or text/xml anyway. A text/html header is how the next file arrives, and that one is not a sitemap.

    /html-body.xml was an HTML page with a 200. This is the shape of a soft 404, a login wall, or a bot challenge. I served the HTML. I did not run a challenge page.

    curl -I http://127.0.0.1:8765/html-body.xml
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    Content-Length: 182
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5
    xmllint on the HTML body
    /tmp/sitemap-lab/body-_html-body.xml:3: parser error : Opening and ending tag mismatch: meta line 3 and head

    xmllint exited 1. If Google fetches this, the documented bucket is a file that is not a supported format, or a parsing error. Both of those are “had errors,” which means the fetch succeeded and the body was useless. Fix: the sitemap URL has to return the sitemap. Not your homepage, not an interstitial.

  4. See whether robots.txt blocks that URL.

    curl cannot see this. The file I disallowed was valid XML, and the server returned 200.

    curl -I http://127.0.0.1:8765/secret-sitemap.xml
    HTTP/1.1 200 OK
    Content-Type: application/xml; charset=utf-8
    Content-Length: 161
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    The robots.txt on the same server:

    robots.txt
    User-agent: *
    Disallow: /secret-sitemap.xml
    
    Sitemap: http://127.0.0.1:8765/ok.xml

    Python 3.12.3 urllib.robotparser read that file and answered:

    urllib.robotparser
    http://127.0.0.1:8765/secret-sitemap.xml can_fetch('*') = False
    http://127.0.0.1:8765/ok.xml can_fetch('*') = True

    Google’s help says it respects robots.txt when fetching sitemaps, and that you have to remove the rule. That is a Couldn’t fetch reason. A 200 from curl does not clear it. Disallow: / blocks the sitemap along with everything else. A rule that only matches the sitemap path does the same to that one URL.

  5. Submit the URL after the redirect.

    The report field for the sitemap URL says redirects are not followed. I served a 301 and a 302. Both pointed at /ok.xml, the file that validated.

    curl -I http://127.0.0.1:8765/old.xml
    HTTP/1.1 301 Moved Permanently
    Content-Type: text/plain; charset=utf-8
    Location: http://127.0.0.1:8765/ok.xml
    Content-Length: 6
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5
    curl -I http://127.0.0.1:8765/temp.xml
    HTTP/1.1 302 Found
    Content-Type: text/plain; charset=utf-8
    Location: http://127.0.0.1:8765/ok.xml
    Content-Length: 6
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    curl -I does not follow the redirect, which is the request you want. Fix: submit http://127.0.0.1:8765/ok.xml, the URL in Location. Do not submit /old.xml and hope. If your host redirects http to https, or the apex to www, the URL in the report has to be the one that returns 200.

  6. Validate the XML and the encoding.

    These all returned 200 with an XML content type. The failure was in the bytes. They matter once a fetch has succeeded. On the help page they are parsing errors, under “Sitemap had errors,” not under Couldn’t fetch.

    An unclosed urlset tag:

    xmllint on the unclosed file
    /tmp/sitemap-lab/body-_broken.xml:6: parser error : Premature end of data in tag urlset line 2

    A raw & in a URL, which XML treats as the start of an entity:

    xmllint on a raw ampersand
    /tmp/sitemap-lab/body-_amp.xml:3: parser error : EntityRef: expecting ';'
      <url><loc>http://127.0.0.1:8765/page?a=1&b=2</loc></url>

    A file declared as UTF-8 with the Latin-1 byte 0xE9 in a comment. xmllint exited 1:

    xmllint on a non-UTF-8 byte
    /tmp/sitemap-lab/body-_latin1.xml:2: parser error : Input is not proper UTF-8, indicate encoding !
    Bytes: 0xE9 0x20 0x2D 0x2D

    The protocol says the file must be UTF-8. Fix: close the tag, escape the ampersand, and save the file as UTF-8. The corrected URL from that file is the same path with an XML entity in place of the raw ampersand:

    escaped query string
    <loc>http://127.0.0.1:8765/page?a=1&amp;b=2</loc>

    Two results that are easy to misread.

    A file with no namespace was well-formed. xmllint’s well-formedness check exited 0. The sitemap schema did not:

    xmllint schema check, no namespace
    /tmp/sitemap-lab/body-_nons.xml:2: element urlset: Schemas validity error : Element 'urlset': No matching global declaration available for the validation root.
    /tmp/sitemap-lab/body-_nons.xml fails to validate

    Google calls that an incorrect namespace. The namespace to put on a sitemap is http://www.sitemaps.org/schemas/sitemap/0.9.

    A file that was the good sitemap with one blank line in front failed xmllint: XML declaration allowed only at the start of the document. Google’s help says leading whitespace will not stop processing. Take the blank line out. Do not treat the xmllint error as proof that Google refused the file.

    One more encoding trap. The same 0xE9 byte, with the declaration changed to ISO-8859-1, passed xmllint and passed Python’s XML parser. Python’s UTF-8 decoder still rejected it: 'utf-8' codec can't decode byte 0xe9 in position 54: invalid continuation byte. A parser that trusts the declaration is not a UTF-8 check. The protocol requires UTF-8. Google’s error list does not give this declaration its own name.

  7. Decompress a gzip sitemap before you trust it.

    The protocol lets you gzip a sitemap. The uncompressed file still has to be at most 50MB. Google’s name for a failed uncompress is “Compression error.” Five ways of serving the same good sitemap:

    A gzip body with Content-Encoding: gzip. The header at 12:16:14 UTC:

    curl -I http://127.0.0.1:8765/gzip-ok.xml
    HTTP/1.1 200 OK
    Content-Type: application/xml
    Content-Encoding: gzip
    Content-Length: 151
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    The raw body started with the bytes 1f8b0800. file said gzip compressed data, from Unix, original size modulo 2^32 161. Python’s ElementTree, which does not decompress, said not well-formed (invalid token): line 1, column 0. After curl was told to honor content encoding, file said XML 1.0 document, ASCII text, and both xmllint and ElementTree accepted it. That is the working form.

    The same gzip bytes with no Content-Encoding. curl left them compressed. ElementTree failed the same way. gzip -t passed, so the gzip itself was valid. A client that believes Content-Type: application/xml and does not sniff gzip never sees a sitemap. xmllint exited 0 on those bytes anyway, because this libxml is built with zlib and sniffs gzip. A clean xmllint run is not proof the response is XML.

    A plain XML body with Content-Encoding: gzip. The header said gzip. file said XML 1.0 document, ASCII text. gzip -t printed gzip: /tmp/sitemap-lab/gz-_gzip-lie.xml: not in gzip format and exited 1. At 12:17:00 UTC I sent that same body and header again so the error text was not hidden:

    curl honoring a false gzip header
    curl: (61) Error while processing content unencoding: incorrect header check

    curl exited 61. Fix: if the body is XML, do not send Content-Encoding: gzip. If the body is gzip, send that header, or serve a .gz file and document it.

    Gzip of gzip, with one Content-Encoding: gzip. One decompress left a body that file still called gzip, magic 1f8b0800, and ElementTree rejected it. A second decompress produced XML that xmllint accepted. One pass was not enough.

    A .gz file served as Content-Type: application/gzip and no content encoding stayed gzip under curl. Same 151 byte payload as the working file. Gunzip it, then parse it. Do not point a parser at the compressed bytes and call the exit code a verdict, unless you know that parser sniffs gzip.

  8. Split a file past 50,000 URLs or 50MB.

    The protocol caps one sitemap at 50,000 URLs and 50MB, which it defines as 52,428,800 bytes. Google’s help repeats both caps: “Too many URLs,” and “Sitemap file size error” for a file larger than 50MB uncompressed. They are separate. I hit each one on its own, and both files were well-formed.

    /too-many.xml had 50,001 loc entries and was 2,700,164 bytes. Under the byte cap. Over the URL cap. xmllint exited 0 in 44ms.

    curl -I http://127.0.0.1:8765/too-many.xml
    HTTP/1.1 200 OK
    Content-Type: application/xml; charset=utf-8
    Content-Length: 2700164
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    /too-big.xml had 29,926 URLs. The first URL was 1,727 characters, under the protocol’s 2,048 character limit, so the size was not a side effect of an illegal URL. The file was 52,430,462 bytes. That is 1,662 bytes over 52,428,800. xmllint exited 0 in 112ms.

    curl -I http://127.0.0.1:8765/too-big.xml
    HTTP/1.1 200 OK
    Content-Type: application/xml; charset=utf-8
    Content-Length: 52430462
    Date: Sat, 24 Oct 2026 12:16:14 GMT
    Connection: keep-alive
    Keep-Alive: timeout=5

    Fix: split into more than one sitemap and list them in a sitemap index. An index has the same two caps, applied to the child sitemaps rather than the page URLs. Gzip does not raise the cap. The limit is the uncompressed size.

  9. Make every URL use the sitemap host and protocol.

    /wrong-urls.xml was well-formed. xmllint exited 0. The schema’s only complaint was the relative URL /page, because a loc under 12 characters fails minLength. It did not mention the other bad URLs.

    The protocol rule is stricter than that schema check. A sitemap may only list URLs that use its protocol and host, and that start with its directory. If the sitemap is on a port, every URL needs that port. I checked the downloaded file against that rule:

    location check for /wrong-urls.xml
    sitemap http://127.0.0.1:8765/wrong-urls.xml
    required prefix http://127.0.0.1:8765/
    DROP https://127.0.0.1:8765/page :: protocol https: != http:; does not start with http://127.0.0.1:8765/
    DROP http://example.com/page :: host example.com != 127.0.0.1:8765; does not start with http://127.0.0.1:8765/
    DROP http://127.0.0.1:9999/page :: host 127.0.0.1:9999 != 127.0.0.1:8765; does not start with http://127.0.0.1:8765/
    DROP /page :: not an absolute URL
    KEEP http://127.0.0.1:8765/page

    A second file lived at /catalog/sitemap.xml. Same check:

    location check for /catalog/sitemap.xml
    sitemap http://127.0.0.1:8765/catalog/sitemap.xml
    required prefix http://127.0.0.1:8765/catalog/
    KEEP http://127.0.0.1:8765/catalog/show
    DROP http://127.0.0.1:8765/images/a :: does not start with http://127.0.0.1:8765/catalog/

    The URL under /catalog/ was kept. The images URL was dropped. That is the protocol’s own example, a sibling folder.

    Google’s names, once it has fetched the file, are “URL not allowed” and the www / protocol path-mismatch errors. https on an http sitemap is the example they give. There is a separate, documented way to list URLs from other verified sites. I did not submit a cross-site sitemap, and a normal file does not get that exception.

    Fix: every loc uses the same scheme, host, and port as the sitemap URL, and sits in that directory or below it. Put the final URL in the sitemap, not a redirect and not a relative path.

What we tested

Checked

One HTTP server on 127.0.0.1 port 8765, requested at 12:16:14 UTC. curl 8.5.0 for the headers. xmllint from libxml 2.9.14 for well-formedness, and the sitemaps.org schemas for the two files that should validate. Python 3.12.3 for UTF-8 checks, ElementTree, and urllib.robotparser. The false gzip error text is from a second request at 12:17:00 UTC with the same body and header. No Search Console property, so no status label from Google’s own screen.

What the test server returned for each sitemap fault, and the fix
FaultWhat came backFix
Good sitemap200, application/xml, schema validNone. This is the control.
XML with a text/html type200, text/html, xmllint passedServe an XML type. Google does not name this header.
HTML page200, text/html, xmllint failedReturn the sitemap file.
robots.txt DisallowHTTP 200, robotparser refused itRemove the rule.
301 and 302Location was the good fileSubmit the final URL.
404 and 500404 Not Found, 500 Internal Server ErrorFix the URL or the server, then resubmit.
Bad XML or encodingUnclosed tag, raw ampersand, and a non-UTF-8 byte each failed xmllintClose tags, escape ampersands, save as UTF-8.
gzip mistakesFalse gzip made curl exit 61. Double gzip was still gzip after one pass.Compress once, and only say gzip when the body is gzip.
50,001 URLs2,700,164 bytes, xmllint passed in 44msSplit. Under 50MB, over the URL cap.
52,430,462 bytes29,926 URLs, xmllint passed in 112msSplit. 1,662 bytes over 52,428,800.
Wrong host or protocolWell-formed. Schema only rejected /page for length.Use the sitemap’s own prefix.
Index child 404Index validated. Child returned 404.Point the index at files that return 200.

When the file is already fine

If the URL returns 200, the body parses, robots.txt allows it, and the URL does not redirect, you have done the checks this test can settle. A new sitemap can still say Couldn’t fetch. The help page tells you the fetch should be immediate and also tells you to wait through a temporary error. The community post says the label often means Google has not fetched it yet, and that a real failure comes with a specific error once Google tries. I cannot show you that screen. I can show you that a clean file is not the same thing as a Success row.

Give it a few days. If it is still Couldn’t fetch and the file is unchanged, resubmit after you have confirmed the live URL. Google stops retrying a sitemap that keeps failing. Resubmitting is how you ask again.

Success is not indexing. A sitemap that reads Success is a list of URLs. How to get a new website indexed by Google is what to do after that, with the other SEO guides. A page Google fetched and then chose not to store is a different report: Crawled, currently not indexed.

If you would rather ask than click around Search Console, ContextBolt SEO, which we make, can check a page’s index status from inside Claude. Add the MCP server to Claude Code first, then ask.

How to get a new website indexed by Google The sitemap is one step. Here is the order for a new domain, and what the Pages report means after Google can fetch the file. Guide Add an MCP server to Claude Code One local stdio server and one remote HTTP server, added with claude mcp add, plus the three scopes and the errors from a real run. Guide

FAQ

What does sitemap could not be read mean?

On the sitemap details page, that line means the fetch failed. Google’s Sitemaps report help says so directly. It does not mean Google parsed your XML and rejected a tag.

Is Couldn’t fetch the same error?

It is the same failure, one level up. The main Sitemaps report says Couldn’t fetch. Open the row and the details page says Sitemap could not be read. A file Google did fetch, with problems inside it, is a different status: Sitemap had errors.

Why does a new sitemap say Couldn’t fetch when the file is fine?

The help page says a sitemap should be fetched immediately, and that some fetch errors are temporary. It also says submitting a sitemap does not guarantee Google will download it. On Google’s own community forum, the long-standing explanation is that a new sitemap can show Couldn’t fetch, and Sitemap could not be read, until Google processes it. That screen lives inside a verified property. The test server below could not reproduce it.

Should I fix the XML first?

No. Request the exact URL and read the status code, the body, robots.txt, and any redirect. Broken XML matters after a fetch. If Google never got the file, a cleaner sitemap does not change the status.

Will fixing the sitemap get my pages indexed?

No. A sitemap that reads Success tells Google which URLs exist. Google’s build-a-sitemap guide says that is a hint, not a promise to index them. Indexing is a separate job, covered in How to get a new website indexed by Google.

Sources

  1. Sitemaps report (Search Console Help) Checked
  2. Build and submit a sitemap (Google Search Central) Checked
  3. Sitemap protocol (sitemaps.org) Checked
  4. Are you seeing couldn't fetch reported for your sitemap? (Google Search Central Community) Checked