hr.invalid_oib
HTTP 400 type: validation_error param: oib | operator_oib | recipient_oib{ "error": { "type": "validation_error", "code": "hr.invalid_oib", "message": "OIB 12345678901 fails the ISO 7064 MOD 11,10 checksum (restrictive error 179). Check it against the company registration.", "param": "oib", "upstream": null, "doc_url": "https://docs.fiskhub.com/errors/hr.invalid_oib" }}What happened
An OIB — the Croatian personal identification number — failed validation. It must be 11 digits whose last digit is an ISO 7064 MOD 11,10 check digit over the first ten.
Croatian restrictive error 179 rejects a message whose OIB fails this check. FiskHub validates it at company creation, so the problem surfaces during configuration rather than at the point of sale.
You will normally see this on POST /v1/companies. param names which OIB failed:
param | Which OIB | When |
|---|---|---|
oib | The company’s own | POST /v1/companies, and on every sale |
operator_oib | OibOper, the operator who issued the receipt (179) | Attended receipts only, and only when the operator is somebody other than the company |
recipient_oib | OibPrimateljaRacuna, the buyer (180) | Attended receipts only |
Restrictive error 180 is the recipient-OIB version of the same rule, and it carries "180"
in upstream.code. It is dormant for unattended sales because that message does not contain a
recipient OIB at all.
Should you retry?
Not unchanged. The checksum is arithmetic; the same digits fail identically forever.
The fix
Check the OIB against the company’s registration documents. A checksum failure means it is not a real OIB, so the number itself is wrong — not its formatting.
The usual causes:
- A transposition. Two adjacent digits swapped. The checksum is designed to catch exactly this, which is why it is worth having.
- A digit dropped or added. Anything other than 11 digits is invalid.
- Formatting characters. Send
"25089460814", not"HR25089460814","250-894-608-14"or a number with spaces. - A JSON number instead of a string.
25089460814is fine numerically, but an OIB with a leading zero loses it. Always send a string.
{ "oib": "25089460814" } // right{ "oib": 25089460814 } // wrong — send a string{ "oib": "HR25089460814" } // wrong — no country prefixChecking one yourself
The algorithm is ISO 7064 MOD 11,10:
function isValidOib(oib) { if (!/^\d{11}$/.test(oib)) return false; let remainder = 10; for (const ch of oib.slice(0, 10)) { remainder = (remainder + Number(ch)) % 10 || 10; remainder = (remainder * 2) % 11; } return (11 - remainder) % 10 === Number(oib[10]);}Validating before submitting turns a round trip into an instant field-level message in whatever form the operator is filling in.
The OIB must also match the certificate
A valid OIB is necessary but not sufficient. The company’s OIB must equal the subject OIB of its
signing certificate, or the Tax Administration rejects every message with s005 — see
hr.certificate_oib_mismatch.
And because the certificate is bound to it, and it is part of every invoice’s business key,
oib cannot be changed after a company is created. A typo caught here is cheap; the same
typo caught after a month of trading means creating a new company and moving its fleet across.