Your extension got rejected and the email barely says why. Here is what Google actually means by each rejection reason, how to fix it, and how the appeal works.
A Chrome Web Store rejection email is deliberately vague. It gives you a policy code and a one-line reason, and it leaves you to work out what part of your extension tripped it. Most rejections are not a judgement that your extension is bad — they are a mismatch between what your extension asks for and what your listing says it does. Close that gap and the resubmission usually goes through.
We ship our own extensions — KeepChats and Gwora — so we have been through this review process from the developer side, not just read about it. Below is what each of the common rejection reasons actually means in Google's terms, the specific change that clears it, and how the appeal works when you think the rejection is wrong.
Almost every rejection maps to one of a handful of policies. The wording in your email will be terse, so the useful move is to translate the policy code into the concrete thing in your package that caused it. This table does that — the reason as Google phrases it, what they are really objecting to, and the change that clears it.
| Rejection reason | What Google actually means | How to fix it |
|---|---|---|
| Broad host permissions / <all_urls> | Your manifest requests access to every site but your extension only needs a few, so the request looks over-scoped and risky. | Narrow host_permissions to the specific domains you use, or switch to activeTab / optional_permissions so access is granted per-use instead of up front. |
| Undisclosed data use / missing privacy policy | You collect or transmit user data but the listing has no privacy policy, or the policy does not cover what the manifest can touch. | Publish a privacy policy URL, complete the data-use disclosures in the dashboard, and make sure both match what your permissions allow. |
| Remotely hosted code | Your extension loads or evaluates JavaScript fetched from a server at runtime, which Manifest V3 bans outright. | Bundle every bit of executable logic inside the package. Remove eval, remote script tags, and any runtime code fetch; ship it all in the .zip. |
| Misleading metadata | The name, description, screenshots, or category oversell or misdescribe what the extension does, or stuff keywords. | Rewrite the listing to describe exactly what the extension does, remove keyword spam and competitor names, and use real screenshots of the actual UI. |
| Single-purpose violation | The extension bundles several unrelated features, so Google cannot identify one narrow purpose as policy requires. | Cut it back to one coherent purpose, or split unrelated features into separate extensions. The listing must describe that single purpose. |
| Minimum functionality | The extension does too little — it is a thin wrapper around a website, a bookmark, or an empty shell — to justify being an extension. | Add real in-browser functionality that a bookmark cannot provide, or reconsider whether it needs to be an extension at all. |
| Permissions not justified by purpose | You request a permission (tabs, scripting, cookies, history) the stated purpose does not obviously need. | Drop every permission you cannot tie to a described feature, and state in the listing why each remaining one is needed. |
The pattern across all of these is the same: Google is checking that your permissions, your code, and your listing tell one consistent story. A rejection is usually them finding a place where one of the three says something the other two do not.
If you only fix one thing before resubmitting, make it the permission list. Broad host permissions — especially <all_urls> or all-sites access — are the single most common reason a review gets held or bounced, because they force a manual reviewer to justify why an extension needs to read every page you visit.
The fix is almost always to narrow the scope. If your extension works on three domains, list those three in host_permissions instead of asking for everything. If it acts on whatever tab the user clicks, activeTab grants access only when they invoke it and needs no broad grant at all. If a permission is only used by an optional feature, move it to optional_permissions so it is requested at the moment it is needed rather than at install. Every permission you remove is one fewer thing a reviewer has to be convinced about.
This is also why a Manifest V3 rewrite can help: migrating from V2 to V3 often lets you replace blocking webRequest with declarativeNetRequest, which drops the broad host access V2 needed — so the V3 version can clear review faster than the original did.
The privacy and data-use rejections catch a lot of developers who do not think of themselves as collecting data at all. If your extension sends anything to a server — even an error report, an analytics ping, or an auth token — that is data use, and the store expects a privacy policy and completed data-use disclosures that cover it.
Two mismatches cause most of these. The first is having no privacy policy URL on a listing whose manifest can touch user data. The second, more subtle, is a privacy policy that exists but does not describe everything the permissions allow — the reviewer compares your manifest against your policy, and a gap between them reads as undisclosed use. Fix both by writing the disclosures against your actual manifest, not against what you think the extension does, and by trimming any permission you cannot account for in the policy.
Not every rejection is correct. Reviews mix automated checks with human reviewers, and a reviewer can misread what a permission is for or flag a feature that genuinely fits policy. When you are confident the rejection is a mistake, you can appeal rather than blindly changing your extension.
How the appeal works. The rejection email links to an appeal form in the Developer Dashboard. Reply to the exact policy cited, not the general area — quote the manifest line, the listing text, or the code path and explain why it complies. Attach specifics: which domains a permission covers and why, where a feature lives, what a data flow does. Appeals are read by a person, so a precise, policy-referenced explanation gets a far better response than a general protest. Expect days, not hours, and do not spam resubmissions while an appeal is open — that slows everything down.
Before appealing, be honest about whether you are right. If the rejection points at a real over-broad permission or a genuine disclosure gap, fixing it and resubmitting is faster than arguing. Save the appeal for when the policy genuinely does not apply to what your extension does — and make that case with evidence, not frustration.
Most rejections are self-fixable. If yours is a missing privacy policy, an over-broad permission you can narrow, or listing copy that oversells, you can clear it in an afternoon and resubmit — no help needed, and you should not pay anyone to do it for you.
Where it is worth bringing in help is when the rejection is structural. A single-purpose violation that means splitting an extension in two, a remotely-hosted-code ban that requires rearchitecting how your extension loads logic, or a permission you cannot remove without rebuilding the feature that depends on it — these are redesigns, not edits, and they are easy to get wrong twice. Repeated rejections also raise scrutiny on your developer account, so getting the resubmission right matters more the second and third time around.
That is the work we do on our own extensions and for clients: auditing a rejected package against the policy it tripped, narrowing permissions to exactly what the code needs, and shipping a resubmission that clears review. If you have a rejection you cannot get past, tell us what your extension does and paste the rejection reason and we will scope the fix against your actual manifest — or point you at the one-line change if that is all it needs.
The most common causes are broad host permissions like <all_urls> that the extension does not need, a missing or incomplete privacy policy for data it collects, remotely hosted code that Manifest V3 bans, misleading listing metadata, a single-purpose violation, or permissions the stated purpose does not justify. Almost all of them come down to a mismatch between what your permissions request, what your code does, and what your listing says — Google rejects when those three do not agree.
Narrow the scope. List only the specific domains your extension acts on in host_permissions instead of requesting <all_urls> or all-sites access. If it works on whatever tab the user clicks, use activeTab so access is granted per-use with no broad grant. Move permissions used only by optional features into optional_permissions so they are requested when needed. Every permission you remove is one fewer thing a reviewer has to justify approving.
Yes. The rejection email links to an appeal form in the Developer Dashboard. Reply to the exact policy cited — quote the manifest line, listing text, or code path and explain why it complies, with specifics about which domains a permission covers and what your data flows do. A person reads it, so a precise, policy-referenced explanation works far better than a general protest. Expect a few days, and do not resubmit repeatedly while an appeal is open.
If your extension sends anything to a server — including error reports, analytics, or auth tokens — yes. The store expects a privacy policy URL and completed data-use disclosures that cover everything your manifest can touch. A frequent rejection cause is a policy that exists but does not describe all the data the permissions allow, because the reviewer compares your manifest against your policy and treats any gap as undisclosed use.
A software studio that ships and maintains its own products — KeepChats, Gwora and Cairn — and builds the same way for clients. Founded and led by codewithumar.
Talk to the studio →Send the problem. You get one fixed number and a plan back within a business day.
We build software worth keeping — for clients, and for ourselves.
Founded & led by codewithumar