# Submission errors

Every problem a check can find, by its code: what it means and what to do. The same words the tool, the portal and the Server use, from one catalog.

Every problem a check finds carries a code, such as `PLG-RECORD-AFTER-TODAY`,
printed with its fix and a link to its entry below, by the tool and by the
portal's "Copy issues" alike. This page is generated from the one catalog all
of them read (`shared/ts/src/data/plugin-store/checks.ts`), so the words here
are the words you saw. A code is never renamed or reused; one no longer used is
marked retired and stays, so an old message still finds its page. Codes from
the portal's own checks are listed as each one ships.

```text norun="what the tool prints for a problem"
Kanboombo plug-in checks: harbour-tides
✖ PLG-RECORD-AFTER-TODAY  Line 6 is dated 2026-10-02, after today (2026-10-01)
  Fix: Usually a time-zone mistake: date your records in the manifest's zone, never with toISOString(). Only a scheduled event may be in the future.
  https://kanboombo.com/developers/plugins/publish/submission-errors/#plg-record-after-today
```

## The manifest {#stage-manifest}

Found in plugin.json, wherever contract 1 is checked: check, the portal, and the Server before every run.

### PLG-INVALID {#plg-invalid}

**Not acceptable to contract 1.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Read the sentence: it names the field or line. Every rule is in the Reference.

### PLG-MANIFEST-INVALID-JSON {#plg-manifest-invalid-json}

**plugin.json is not JSON.** Error; since 2026-10-01.

It says: `plugin.json could not be read as JSON: {detail}`

What to do: Check for a trailing comma or an unquoted key. Your editor can validate it against plugin.schema.json.

### PLG-MANIFEST-NOT-OBJECT {#plg-manifest-not-object}

**The manifest is not an object.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: plugin.json must hold one JSON object: { "contract": 1, "id": …, … }.

### PLG-MANIFEST-UNKNOWN-KEY {#plg-manifest-unknown-key}

**A key contract 1 does not know.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Remove the key, or check its spelling against the Reference. Unknown keys are refused so a typo never passes silently.

### PLG-MANIFEST-CONTRACT {#plg-manifest-contract}

**Wrong contract.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Set "contract": 1.

### PLG-MANIFEST-ID {#plg-manifest-id}

**The id is not allowed.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Use 2 to 48 lowercase letters, digits and hyphens, starting with a letter: "harbour-almanac". It is also the folder's name.

### PLG-MANIFEST-NAME {#plg-manifest-name}

**A name is missing or too long.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Give each name per language, 80 characters or fewer: { "en": "Harbour Almanac", "es": "Almanaque del puerto" }.

### PLG-MANIFEST-VERSION {#plg-manifest-version}

**The version is not major.minor.patch.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Use three numbers, such as 1.0.0, and raise it with every change you publish.

### PLG-MANIFEST-TIMEZONE {#plg-manifest-timezone}

**Not a named time zone.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Use an IANA zone such as America/Santo_Domingo, never an offset like -04:00. It decides your plug-in's today.

### PLG-MANIFEST-AUDIENCE {#plg-manifest-audience}

**Unknown audience.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Use public, private or local.

### PLG-MANIFEST-SCHEDULE {#plg-manifest-schedule}

**The schedule is not a five-field cron.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Write minute, hour, day of month, month and day of week, read in your time zone: "30 9 \* \* 1-5" is 09:30 on weekdays.

### PLG-MANIFEST-BACKFILL {#plg-manifest-backfill}

**backfill is not all or a span.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Use "all", or a span such as "5y" or "90d": how far back your first run fills.

### PLG-MANIFEST-SOURCES {#plg-manifest-sources}

**A source is not declared correctly.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Each source needs a lowercase name, used once, and an https url; maxBytes is at most 20 MB. At most 16 sources.

### PLG-MANIFEST-SOURCE-URL {#plg-manifest-source-url}

**A source is not an https URL.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Kanboombo downloads only https URLs with a lowercase host. {today}, {year} and {month} are filled in your time zone.

### PLG-MANIFEST-SERIES {#plg-manifest-series}

**A series is not declared correctly.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Each series needs a key, a kind (value, event, item), a cadence, its dates (observation or publication) and a name. Only value series have fields.

### PLG-MANIFEST-LINKS {#plg-manifest-links}

**links.hosts is not a list of host names.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: List the lowercase hosts your items may link to besides your sources' own, such as "www.bancentral.gov.do". At most 16.

### PLG-MANIFEST-LIMITS {#plg-manifest-limits}

**A limit is out of range.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: limits may only lower the defaults: seconds 1 to 120, megabytes 16 to 256.

### PLG-MANIFEST-LICENCE {#plg-manifest-licence}

**The licence block is incomplete.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Name the holder, the attribution shown beside your records (40 characters or fewer), the terms URL (or null) and whether the data may be kept: yes, no or unknown.

### PLG-ID-RESERVED {#plg-id-reserved}

**That id is reserved.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Choose another id: words the store's own addresses use (store, publish, days…), and ids starting kanboombo, kb-, chk- or test-, are taken.

### PLG-LISTING-INVALID {#plg-listing-invalid}

**listing.json is not a listing.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Start from the listing in Reference › Listing: "listing": 1, then the keys it names and nothing else.

### PLG-LISTING-ID {#plg-listing-id}

**The listing is for another plug-in.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Set "plugin" to your manifest's id.

### PLG-LISTING-PUBLISHER {#plg-listing-publisher}

**No publisher domain.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Name the domain your organization verifies, such as { "domain": "bancodata.do" }. The store shows it beside your name.

### PLG-LISTING-TAGLINE {#plg-listing-tagline}

**The tagline is missing or too long.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: One line, 80 characters or fewer per language, that says what lands on your readers' days.

### PLG-LISTING-DESCRIPTION {#plg-listing-description}

**The description is missing or too long.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Plain text, 2,000 characters or fewer per language, paragraphs separated by a blank line. Your readers are short of time.

### PLG-LISTING-LANGUAGE-MISSING {#plg-listing-language-missing}

**No English.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Write the tagline and description in English (en) too; add your other languages beside it.

### PLG-LISTING-CATEGORY {#plg-listing-category}

**Unknown category.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Pick the one category readers would look in first.

### PLG-LISTING-KEYWORDS {#plg-listing-keywords}

**Too many keywords.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: At most 12 words or short phrases a reader might search for, 30 characters each.

### PLG-LISTING-LINK {#plg-listing-link}

**A link is not https.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Every link in a listing is https.

### PLG-NO-PRIVACY-POLICY {#plg-no-privacy-policy}

**No privacy policy.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Link a page that says who you are and what your plug-in collects: with contract 1, nothing about your readers.

### PLG-NO-SUPPORT-CONTACT {#plg-no-support-contact}

**No way to reach you.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Give links.support a url, an email, or both.

### PLG-LISTING-SOURCE {#plg-listing-source}

**A listed source is wrong.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Describe only sources your manifest declares: a title, the page it comes from, and its licence.

### PLG-LISTING-EXAMPLE {#plg-listing-example}

**An example day is wrong.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Name 1 to 3 days your fixtures cover. The store draws each one from your plug-in's own output, so pick days that show what it does.

### PLG-LISTING-ICON {#plg-listing-icon}

**The icon is not right.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: A 256×256 PNG, 64 KB or less, that still reads at 14 pixels.

## A run's records {#stage-records}

Found in what fetch.mjs prints. The Server refuses the whole batch on any of these; nothing from the run is written.

### PLG-RECORD-TODAY {#plg-record-today}

**The run had no today.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: This is Kanboombo's side, not yours: the run context lacked a valid today. Run the checks again.

### PLG-RECORD-TOO-MANY {#plg-record-too-many}

**Too many records in one run.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Print at most 250,000 records per run. A larger history fills over several runs: set backfill to a span.

### PLG-RECORD-INVALID {#plg-record-invalid}

**A record breaks contract 1.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: The sentence names the line. Compare it with Reference › Records: every value is a finite number, every date YYYY-MM-DD.

### PLG-RECORD-SERIES {#plg-record-series}

**A record for a series the manifest does not declare.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Print only the series plugin.json declares, each with its declared kind.

### PLG-RECORD-UNKNOWN-KEY {#plg-record-unknown-key}

**A record has a key contract 1 does not know.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Each kind has its own keys (Reference › Records). Remove the extra one.

### PLG-RECORD-AFTER-TODAY {#plg-record-after-today}

**A record dated after today.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Usually a time-zone mistake: date your records in the manifest's zone, never with toISOString(). Only a scheduled event may be in the future.

### PLG-RECORD-LINK {#plg-record-link}

**An item links somewhere it may not.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Item links are https, to a source's host or one listed in links.hosts.

### PLG-RECORD-REPEATED {#plg-record-repeated}

**The same record twice in one run.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: A record's key is its series, date and period (an event's or item's id). Print each once.

## Your folder {#stage-check}

Found by `check` (kanboombo-plugin.mjs or npm run plugin) on your machine, and again when you upload.

### PLG-FOLDER-ID {#plg-folder-id}

**The folder is not named after the id.** Error; since 2026-10-01.

It says: `The folder is named {folder}, but the manifest's id is {id}`

What to do: Rename the folder, or the id: they must match.

### PLG-FETCH-MISSING {#plg-fetch-missing}

**No fetch.mjs.** Error; since 2026-10-01.

It says: `There is no fetch.mjs in the folder`

What to do: The fetcher is always fetch.mjs at the top of the folder. kanboombo-plugin.mjs init writes one.

### PLG-IMPORT-OUTSIDE {#plg-import-outside}

**An import from outside the plug-in.** Error; since 2026-10-01.

It says: `{file} imports {spec}, outside the plug-in's folder`

What to do: A plug-in is self-contained: copy what you need into the folder.

### PLG-IMPORT-PACKAGE {#plg-import-package}

**An import of an undeclared package.** Error; since 2026-10-01.

It says: `{file} imports {spec}, which package.json does not declare`

What to do: Import a Node builtin with its node: prefix (node:fs, never fs), or copy the code into the folder as a readable .mjs file.

### PLG-FIXTURE-SOURCE-MISSING {#plg-fixture-source-missing}

**A source has no fixture.** Error; since 2026-10-01.

It says: `There is no fixtures/sources/{source} for the source "{source}"`

What to do: Save a real copy of each declared source in fixtures/sources/, named after it. Checks run on these, never on the network.

### PLG-FIXTURE-CONTEXT-INVALID {#plg-fixture-context-invalid}

**fixtures/context.json is not valid.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Write { "now": "2026-10-01T13:30:00Z", "today": "2026-10-01" }, where today is now's date in your time zone.

### PLG-FIXTURE-RECORDS-MISSING {#plg-fixture-records-missing}

**No fixtures/records.jsonl.** Error; since 2026-10-01.

It says: `There is no fixtures/records.jsonl to compare with`

What to do: Run check with --update once to write it, read it, and keep it.

### PLG-FIXTURE-MISMATCH {#plg-fixture-mismatch}

**The output differs from fixtures/records.jsonl.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: If the change is intended, run check with --update and read the difference before you keep it.

### PLG-RUN-FAILED {#plg-run-failed}

**The fetcher exited with an error.** Error; since 2026-10-01.

It says: `fetch.mjs exited {exit}: {reason}`

What to do: The reason is the last line it wrote to stderr. Exit non-zero only when a source is not what you expect.

### PLG-RUN-KILLED {#plg-run-killed}

**The fetcher was stopped at its limits.** Error; since 2026-10-01.

It says: `fetch.mjs was stopped ({signal}) at its limits`

What to do: It ran past its seconds or memory. Read the source once, stream large files, and print as you go.

### PLG-RUN-NETWORK {#plg-run-network}

**The fetcher tried the network.** Error; since 2026-10-01.

It says: `{reason}`

What to do: Plug-ins have no network. Declare the file as a source in plugin.json and read it from the path the context gives.

### PLG-RUN-EXPECTED-FAILURE {#plg-run-expected-failure}

**A run that should fail did not fail as expected.** Error; since 2026-10-01.

It says: `fixtures/runs/{run} expects {expected}, but the fetcher {actual}`

What to do: A broken source must stop the run, so nothing wrong is written: exit with expect.json's exit, and write a last stderr line that contains its reason. If it stopped and only the words differ, make the reason and that line agree.

### PLG-RUN-READ-OUTSIDE {#plg-run-read-outside}

**The fetcher read a file outside its folder.** Error; since 2026-10-01.

It says: `fetch.mjs tried to read {path}, outside its folder and its sources`

What to do: Read only your own folder and the paths the run context gives in sources. On the Server nothing else is there to read.

## The upload {#stage-upload}

Found when the portal reads your ZIP, before anything of it runs.

### PLG-ID-TAKEN {#plg-id-taken}

**Another publisher has that id.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Choose another id. An id belongs to the organization that first used it, and is never reused.

### PLG-ID-RETIRED {#plg-id-retired}

**That id was retired.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: A retired id stays retired: readers' records are kept under it. Choose a new id.

### PLG-STORE-AUDIENCE {#plg-store-audience}

**Store plug-ins are public.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Set "audience": "public". A private plug-in is set up for its readers at host.kanboombo.com, not published in the store.

### PLG-LICENCE-TERMS-MISSING {#plg-licence-terms-missing}

**No terms for the data.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Link the terms the data is published under in licence.terms. The store shows them, and review reads them.

### PLG-SCHEDULE-TOO-FREQUENT {#plg-schedule-too-frequent}

**It would run too often.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: A plug-in may run at most 24 times a day. Most sources change once or twice a day: match yours.

### PLG-QUOTA-UPLOADS {#plg-quota-uploads}

**Too many uploads today.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: An organization uploads at most 20 versions a day. Check locally with kanboombo-plugin.mjs check first; it runs the same checks.

### PLG-ZIP-TOO-LARGE {#plg-zip-too-large}

**The archive is too large.** Error; since 2026-10-01.

It says: `The archive is {size}; an upload may be at most {limit}`

What to do: Leave out what the plug-in does not need: trim large fixtures to the rows your checks read, and never include node_modules/.

### PLG-ZIP-NOT-ZIP {#plg-zip-not-zip}

**Not a ZIP archive.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Upload the .zip that kanboombo-plugin.mjs pack writes, or compress the plug-in's folder. If the upload was cut short, try again.

### PLG-ZIP-PREPENDED {#plg-zip-prepended}

**Something before the archive.** Error; since 2026-10-01.

It says: `Something comes before the archive's first file, as in a self-extracting archive or a file with a ZIP glued on`

What to do: Upload a plain .zip: compress the plug-in's folder, or use kanboombo-plugin.mjs pack.

### PLG-ZIP-MULTI-DISK {#plg-zip-multi-disk}

**A split archive.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Make one .zip file, not a split or spanned archive. A plug-in is small enough for one.

### PLG-ZIP-ZIP64 {#plg-zip-zip64}

**A ZIP64 archive.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: A plug-in is small enough for a plain ZIP. Compress the folder with your system's own tool, or with kanboombo-plugin.mjs pack, rather than streaming it through zip.

### PLG-ZIP-ENCRYPTED {#plg-zip-encrypted}

**An encrypted file.** Error; since 2026-10-01.

It says: `{name} is encrypted`

What to do: Upload the archive without a password. Nothing in a plug-in is secret: a person reads every file before it is published.

### PLG-ZIP-METHOD {#plg-zip-method}

**A compression Kanboombo does not read.** Error; since 2026-10-01.

It says: `{name} is compressed with method {method}; Kanboombo reads deflate (8) and stored (0) only`

What to do: Compress with deflate, what every common zip tool uses by default; turn off bzip2, LZMA or deflate64.

### PLG-ZIP-TOO-MANY-ENTRIES {#plg-zip-too-many-entries}

**Too many files.** Error; since 2026-10-01.

It says: `The archive has {count} entries; the most is {limit}`

What to do: Keep a few fixtures that show each case rather than every day's file.

### PLG-ZIP-UNCOMPRESSED-TOO-LARGE {#plg-zip-uncompressed-too-large}

**The archive unpacks too large.** Error; since 2026-10-01.

It says: `The archive unpacks to {size}; the most is {limit}`

What to do: Trim your fixtures to the rows your checks read. The Server downloads the real sources; the archive only carries samples.

### PLG-ZIP-RATIO {#plg-zip-ratio}

**A file compresses suspiciously well.** Error; since 2026-10-01.

It says: `{name} unpacks to {ratio} times its compressed size; the most is {limit} times`

What to do: That is how a ZIP bomb looks. If the file is real, shorten it: very repetitive sample files can keep only the rows your checks read.

### PLG-ZIP-DAMAGED {#plg-zip-damaged}

**A damaged archive.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Compress the folder again and upload the new file. If it keeps happening, use kanboombo-plugin.mjs pack, which writes archives Kanboombo always reads.

### PLG-ZIP-PATH {#plg-zip-path}

**A file name Kanboombo does not accept.** Error; since 2026-10-01.

It says: `{name}: {detail}`

What to do: Name files and folders with letters, digits, ".", "_", "-" and "$", each part starting with a letter, a digit or "$": no "..", no leading "/", no "\\", at most 200 characters and 8 parts.

### PLG-ZIP-DUPLICATE {#plg-zip-duplicate}

**One name used twice.** Error; since 2026-10-01.

It says: `{name} clashes with {other}: the same name twice, names that differ only in case, or a file where a folder is`

What to do: Keep one of them. Names that differ only in case are one file on a Mac or a Windows computer, so they can never both be what you tested.

### PLG-ZIP-SYMLINK {#plg-zip-symlink}

**A symbolic link.** Error; since 2026-10-01.

It says: `{name} is a symbolic link`

What to do: Put the file itself in the folder. What runs must be what was reviewed, and a link can point anywhere.

### PLG-ZIP-NOT-A-FILE {#plg-zip-not-a-file}

**Something that is not a plain file.** Error; since 2026-10-01.

It says: `{name} is {kind}`

What to do: A plug-in holds plain files and folders only. Compress the folder again from a normal copy of it.

### PLG-ZIP-NO-MANIFEST {#plg-zip-no-manifest}

**No plugin.json.** Error; since 2026-10-01.

It says: `The archive has no plugin.json at its top, or in one folder that holds everything else`

What to do: Compress the plug-in's folder itself, so plugin.json is at the top of the archive or inside a single folder named after your id.

### PLG-ZIP-DROPPED {#plg-zip-dropped}

**Files your computer adds were left out.** Note; since 2026-10-01.

It says: `Left out what your computer adds: {names}`

What to do: Nothing to do: __MACOSX/ and .DS_Store are never part of a plug-in.

### PLG-ZIP-CODE-TOO-LARGE {#plg-zip-code-too-large}

**The code is too large.** Error; since 2026-10-01.

It says: `Your code is {size}; the most is {limit}`

What to do: Code is everything outside fixtures/, listing/, listing.json, review.json and README files. Keep fetchers small and readable, and move sample data into fixtures/.

### PLG-FILE-HIDDEN {#plg-file-hidden}

**A hidden file.** Error; since 2026-10-01.

It says: `{name} is hidden: its name starts with a dot`

What to do: Leave out hidden files such as .env, .git/ or .npmrc. Nothing secret belongs in a plug-in, and a reviewer must see every file.

### PLG-FILE-TYPE {#plg-file-type}

**A kind of file the code may not hold.** Error; since 2026-10-01.

It says: `{name} is not a .mjs, .json, .md or .txt file or a LICENSE`

What to do: Your code is readable ES modules (.mjs), JSON, text and a licence. Sample data goes in fixtures/, the icon in listing/.

### PLG-DEPENDENCIES {#plg-dependencies}

**Packages in a store plug-in.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Copy what you need into the folder as readable .mjs files (plugins/dop-economy/xlsx.mjs reads XLSX that way), remove node_modules/, and declare no dependencies.

### PLG-VERSION-NOT-GREATER {#plg-version-not-greater}

**Not newer than what is published.** Error; since 2026-10-01.

It says: `Version {version} is not greater than {published}, which is already published`

What to do: Raise the version in plugin.json above every published one, then upload again.

## The locked room {#stage-room}

Found when the Server runs your fixtures, twice, and each fixtures/runs case, in the locked room.

### PLG-CHECK-INFRA {#plg-check-infra}

**The Server could not finish the checks.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Kanboombo's side, not yours: run the checks again. If it keeps happening, write to support.

### PLG-ROOM-RUN-FAILED {#plg-room-run-failed}

**It failed in the locked room.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Run kanboombo-plugin.mjs check on the same fixtures. The room has no network, Node 24, and only your folder and its sources.

### PLG-ROOM-MISMATCH {#plg-room-mismatch}

**In the locked room it printed something else.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Your fetcher depends on something the room does not have: the time, an environment variable, a file outside the folder. Print only what the sources say.

### PLG-ROOM-NONDETERMINISTIC {#plg-room-nondeterministic}

**Two runs printed different records.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: The same sources must print the same records: no clock, no randomness. Snapshot windows compare runs, so a changing output would revise records every run.

### PLG-ROOM-EXPECTED-FAILURE {#plg-room-expected-failure}

**A broken source did not stop the run.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: A run on a broken source must exit non-zero with a reason, so nothing wrong is written.

### PLG-EXAMPLE-MISMATCH {#plg-example-mismatch}

**An example day is not what the locked room printed.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Your review information says what a reader sees on each example day; the room ran your fixtures and printed something else. Correct the example, or the fetcher, so the two agree.

## The live sources {#stage-probe}

Found when the Server downloads your real sources once, and asks each page your listing links to.

### PLG-SOURCE-UNREACHABLE {#plg-source-unreachable}

**A source did not answer.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Check the URL from outside your network; then run the checks again. A source that needs a login cannot be a source: contract 1 has no secrets.

### PLG-PROBE-RUN-FAILED {#plg-probe-run-failed}

**It failed on today's real sources.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Your fixtures pass, but the real file is different now. Download it, add it to fixtures, and read it the way it is.

### PLG-PROBE-EXAMPLE-DIFFERS {#plg-probe-example-differs}

**Today's source says something else for an example day.** Warning; since 2026-10-01.

Its message names the field or the line.

What to do: Sources revise. If the new figure is right, update your example days and fixtures.

### PLG-LINK-UNREACHABLE {#plg-link-unreachable}

**A page you link to did not answer.** Warning; since 2026-10-01.

Its message names the field or the line.

What to do: Readers and reviewers follow these links: your privacy policy, support page, homepage, and each source's page and terms. Make sure each answers with a page over https.

## Submitting {#stage-submit}

Found when you submit for review.

### PLG-REVIEW-EXAMPLES {#plg-review-examples}

**Five example days are needed.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Give five days your plug-in puts something on, each with its series and the values a reader sees there. The checks run your plug-in and compare.

### PLG-REVIEW-NEGATIVES {#plg-review-negatives}

**Three cases where nothing should appear are needed.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Give three: a day with no record (a holiday), a day showing the value in force (a weekend), and at least one broken source from fixtures/runs/\<case\>/ (with an expect.json) that must stop the run.

### PLG-REVIEW-SOURCE-TERMS {#plg-review-source-terms}

**A source's terms are missing.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: For each source, name its holder, link its terms, and say on what basis you publish it: an open licence, permission (with where it was given), the public domain or your own data.

### PLG-REVIEW-RELEASE-NOTES {#plg-review-release-notes}

**Release notes are needed for an update.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Write a sentence or two, said to your readers, on what this version adds or fixes: 500 characters or fewer per language.

### PLG-REVIEW-INFO {#plg-review-info}

**The review information is not acceptable.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Notes for the reviewer are plain text of 2,000 characters or fewer, and a video link is https.

### PLG-ATTESTATIONS-MISSING {#plg-attestations-missing}

**Not every statement is confirmed.** Error; since 2026-10-01.

It says: `Confirm: {missing}`

What to do: Read each statement and tick it. A version goes to review only when all of them hold.

### PLG-AGREEMENT-NOT-ACCEPTED {#plg-agreement-not-accepted}

**The Developer Agreement is not accepted.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: An owner or admin accepts it at kanboombo://publish › Publisher. A new version is accepted again before your next submission.

### PLG-PUBLISHER-NOT-VERIFIED {#plg-publisher-not-verified}

**Your domain is not verified.** Error; since 2026-10-01.

It says: `Your organization's domain is {status}`

What to do: Verify your domain at kanboombo://publish › Publisher, with a DNS record or a file on your site. Until then you can upload and run checks, but not submit or publish.

### PLG-INTAKE-NOT-INVITED {#plg-intake-not-invited}

**Submissions are by invitation.** Error; since 2026-10-01.

It says: `Kanboombo is taking plug-ins from invited organizations first, and yours is not invited yet`

What to do: Ask Kanboombo for an invitation. Meanwhile you can upload, run checks and prepare your review information.

## Your organization {#stage-publisher}

Found when your organization verifies its domain in the portal.

### PLG-PUBLISHER-DOMAIN-INVALID {#plg-publisher-domain-invalid}

**Not a domain name.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Type the domain your organization's site is on, such as bancodata.do: no https://, no path, no port.

### PLG-PUBLISHER-DOMAIN-IP {#plg-publisher-domain-ip}

**An IP address.** Error; since 2026-10-01.

It says: `{domain} is an IP address, not a domain name`

What to do: Verify a domain name. Readers recognise a name; an address proves nothing about who you are.

### PLG-PUBLISHER-DOMAIN-SINGLE-LABEL {#plg-publisher-domain-single-label}

**A name with no dot.** Error; since 2026-10-01.

It says: `{domain} is not a public domain name`

What to do: Use the full domain, such as bancodata.do.

### PLG-PUBLISHER-DOMAIN-SHARED {#plg-publisher-domain-shared}

**A shared host.** Error; since 2026-10-01.

It says: `{domain} is on {suffix}, where anyone can have a site`

What to do: Verify a domain your organization owns. A site on a shared host proves only that you have an account there.

### PLG-PUBLISHER-DOMAIN-TAKEN {#plg-publisher-domain-taken}

**Another organization verified this domain.** Error; since 2026-10-01.

It says: `{domain} is verified by another organization`

What to do: If the domain is yours, ask whoever verified it to release it, or write to Kanboombo. One domain publishes for one organization.

### PLG-PUBLISHER-DNS-NO-RECORD {#plg-publisher-dns-no-record}

**No verification record yet.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Add the TXT record exactly as shown. DNS can take an hour to reach everyone; then check again.

### PLG-PUBLISHER-DNS-TIMEOUT {#plg-publisher-dns-timeout}

**DNS did not answer.** Error; since 2026-10-01.

It says: `{name} did not answer within {seconds} seconds`

What to do: Check again in a few minutes. If it keeps happening, ask whoever runs your DNS.

### PLG-PUBLISHER-DNS-FAILED {#plg-publisher-dns-failed}

**DNS refused the question.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Check that the domain exists and its DNS answers, then check again.

### PLG-PUBLISHER-FILE-UNREACHABLE {#plg-publisher-file-unreachable}

**The verification file could not be read.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: Put the file at https://\<your domain\>/.well-known/kanboombo-verify.txt, served over https with a valid certificate and no sign-in.

### PLG-PUBLISHER-FILE-MISMATCH {#plg-publisher-file-mismatch}

**The verification file does not hold your token.** Error; since 2026-10-01.

Its message names the field or the line.

What to do: The file holds one line: kanboombo-verify= and your token, exactly as shown.

## In the store {#stage-publish}

Found when you unpublish a plug-in, list it again or retire it in the portal.

### PLG-LISTING-NOT-PUBLISHED {#plg-listing-not-published}

**Not in the store yet.** Error; since 2026-10-07.

It says: `{plugin} has no version published through the portal`

What to do: Publish a version first. Unpublish, List again and Retire act on a plug-in that is in the store.

### PLG-LISTING-HELD {#plg-listing-held}

**Kanboombo has it off the shelf.** Error; since 2026-10-07.

It says: `Kanboombo took {plugin} off the store's shelf itself, so it is not yours to unpublish or list again`

What to do: Ask Kanboombo, who unlisted it, to list it again. You can still retire it.

### PLG-LISTING-TAKEN-DOWN {#plg-listing-taken-down}

**Kanboombo took it down.** Error; since 2026-10-07.

It says: `Kanboombo took {plugin} down, so only Kanboombo can bring it back`

What to do: Read the message Kanboombo sent your owners and admins, fix what it names and answer it. Kanboombo restores the same code, or a new version goes through review.

### PLG-LISTING-RETIRED {#plg-listing-retired}

**It is retired.** Error; since 2026-10-07.

It says: `{plugin} is retired: its runs stopped for good and its id is never used again`

What to do: Nothing brings a retired plug-in back. Publish what replaces it under a new id.

### PLG-RETIRE-CONFIRM {#plg-retire-confirm}

**Type the id to retire it.** Error; since 2026-10-07.

It says: `To retire it, type its id, {plugin}, exactly`

What to do: Retiring stops its runs for good and its id is never used again, so you type the id to say you mean it: lowercase, with its dashes.
