<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: hexstack</title>
    <description>The latest articles on DEV Community by hexstack (@hexstack_app).</description>
    <link>https://dev.to/hexstack_app</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4096587%2Fbca9f5fb-a90e-49ba-addb-f778ecd6d692.jpg</url>
      <title>DEV Community: hexstack</title>
      <link>https://dev.to/hexstack_app</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hexstack_app"/>
    <language>en</language>
    <item>
      <title>Google silently rejected my JobPosting markup for months — the field was one word wrong</title>
      <dc:creator>hexstack</dc:creator>
      <pubDate>Thu, 27 Aug 2026 04:00:02 +0000</pubDate>
      <link>https://dev.to/hexstack_app/google-silently-rejected-my-jobposting-markup-for-months-the-field-was-one-word-wrong-4jio</link>
      <guid>https://dev.to/hexstack_app/google-silently-rejected-my-jobposting-markup-for-months-the-field-was-one-word-wrong-4jio</guid>
      <description>&lt;p&gt;I run &lt;a href="https://linkedinsalaries.com" rel="noopener noreferrer"&gt;linkedinsalaries.com&lt;/a&gt;, a small site that indexes &lt;strong&gt;only&lt;/strong&gt; the LinkedIn job postings which actually state a salary, normalises every figure to USD/month, and throws the rest away. Roughly 800 jobs at a time, 101 locales, static build on a Cloudflare Worker.&lt;/p&gt;

&lt;p&gt;Last week Search Console told me something I'd been getting wrong since launch, and the failure mode is worth sharing because nothing in my own tests caught it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The error
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Invalid object type for field &lt;code&gt;applicantLocationRequirements&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Critical issues prevent your page or feature from appearing in Search results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every remote job on the site was ineligible for the Google Jobs experience. Silently. For months.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I had written
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JobPosting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jobLocationType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TELECOMMUTE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"applicantLocationRequirements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AdministrativeArea"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Europe, Middle East &amp;amp; Africa"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks reasonable. &lt;code&gt;AdministrativeArea&lt;/code&gt; is a real schema.org type, the JSON is valid, and a generic schema validator passes it happily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's wrong
&lt;/h2&gt;

&lt;p&gt;Google's JobPosting spec accepts &lt;strong&gt;only &lt;code&gt;Country&lt;/code&gt;&lt;/strong&gt; for that field, and states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At least one country is required if the job is 100% remote.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My &lt;code&gt;region&lt;/code&gt; values aren't countries — they're multi-country &lt;em&gt;zones&lt;/em&gt; (EMEA, Americas, APAC, worldwide-remote). So there was no single country name to put there. The correct shape is an &lt;strong&gt;array&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"applicantLocationRequirements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Country"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GB"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Country"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Country"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FR"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used ISO alpha-2 codes rather than country names, because the site renders in 101 locales and a translated country name would change per locale — the markup shouldn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things I took away
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. "Valid schema.org" and "valid for Google" are different bars.&lt;/strong&gt; The schema.org validator reported zero errors on the broken version. Google's requirements are a &lt;em&gt;stricter subset&lt;/em&gt;, and only Search Console tells you about the subset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. An absent optional property beats a malformed one.&lt;/strong&gt; For an unrecognised region I now emit nothing at all rather than guessing. A missing optional field is valid; a wrong type is a critical error that removes the page from a feature entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. If &lt;code&gt;jobLocation&lt;/code&gt; is present, &lt;code&gt;addressCountry&lt;/code&gt; is mandatory.&lt;/strong&gt; I'd been emitting &lt;code&gt;PostalAddress&lt;/code&gt; objects where every field was &lt;code&gt;undefined&lt;/code&gt; because the source data is free text like "Berlin, Germany". Now I parse what's actually knowable (locality, region, country) and drop &lt;code&gt;jobLocation&lt;/code&gt; entirely when no country can be determined. I deliberately do &lt;strong&gt;not&lt;/strong&gt; synthesise &lt;code&gt;streetAddress&lt;/code&gt; or &lt;code&gt;postalCode&lt;/code&gt; — a guessed postcode is worse than an omitted optional field.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually stung
&lt;/h2&gt;

&lt;p&gt;I found this in Search Console, not in my own tests — because my test asserted the &lt;em&gt;shape of my config&lt;/em&gt;, not the &lt;em&gt;behaviour of the output&lt;/em&gt;. It checked that I'd typed what I meant to type. It passed happily while the markup was unusable.&lt;/p&gt;

&lt;p&gt;So I wrote an audit that fetches the &lt;strong&gt;deployed&lt;/strong&gt; HTML, parses the real JSON-LD, and asserts &lt;code&gt;applicantLocationRequirements&lt;/code&gt; is &lt;code&gt;Country&lt;/code&gt; on every posting. Running it against production before deploying the fix reproduced all six known issues independently. That's the property I want from a test: it should fail when reality is broken, not when my intent is mis-typed.&lt;/p&gt;

&lt;p&gt;If you're publishing JobPosting markup, check that one field. It fails quietly and costs you the entire Google Jobs surface.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The site is free, no account, and links straight to the original LinkedIn posts. There are regional Telegram feeds too (&lt;a href="https://t.me/linkedin_salaries_remote" rel="noopener noreferrer"&gt;worldwide remote&lt;/a&gt;, &lt;a href="https://t.me/linkedin_salaries_central" rel="noopener noreferrer"&gt;EMEA&lt;/a&gt;, &lt;a href="https://t.me/linkedin_salaries_west" rel="noopener noreferrer"&gt;Americas&lt;/a&gt;, &lt;a href="https://t.me/linkedin_salaries_east" rel="noopener noreferrer"&gt;Asia &amp;amp; Oceania&lt;/a&gt;) if you'd rather be pushed the postings than check a page. Honest caveat: only the last 7 days are kept, since cards point at live listings and a dead link wastes your time.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>google</category>
      <category>seo</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
