Validation issues
When Owlie checks a connector (on import, when you save a draft through the API, before a test run,
and when you publish), it reports each problem as an issue: a code (the kind of problem, such as
MISSING_METHOD), a requirement (what failed, such as grant.team_member.remove), a message
and, where it applies, a path (the file or manifest location). The Connector Builder shows them
under Problems and in the publish dialog; the API returns them as
issues { code requirement message path }.
Rows whose code is ValidationError are not issues: they are the error Owlie returns when it refuses
a call, which then writes nothing. Through GraphQL, the error's extensions.code is ValidationError
(or the other code the row names) and its message says what failed; the requirement is not part of a
GraphQL error. Through /api/rpc, a refused test run carries its requirement in
error.details.requirement. Build with the API and agents shows both
shapes.
Most issues appear as soon as you save or import. The authentication hook rows and the
definition.* rows are checked when Owlie loads the built connector, so they can first appear when a
test runs the draft.
Requirements below use placeholders: <entity> an entity key, <kind> a Grant kind key, <option>
an authentication option key, <handler> a handler key, <hook> an authentication hook name, <path>
a file path, JSON pointer or schema path, <field> a field name, <n> a list position.
Handlers
| Requirement | Code | Means | Fix |
|---|---|---|---|
testConnection | MISSING_METHOD | The connector has no connection.testConnection. | Add handlers/connection/testConnection.ts. |
entity.<entity>.fetchOne | MISSING_METHOD | Every entity needs fetchOne. | Add handlers/<entity>/fetchOne.ts. |
entity.<entity>.fetchAll | MISSING_METHOD | The connector provides sync, so every entity needs fetchAll. | Add handlers/<entity>/fetchAll.ts. |
entity.<entity>.fetchAllSince | MISSING_METHOD | The entity declares supports_incremental: true. | Add fetchAllSince, or set supports_incremental to false. |
entity.<entity>.fetchRelationships | MISSING_METHOD | A per-entity Grant kind is discovered via this entity. | Add handlers/<entity>/fetchRelationships.ts, or change the Grant kind's fetch_mode. |
entity.<entity>.create | MISSING_METHOD, INVALID_ENTITY | The connector provides provision and the account has no create; or the account declares create_semantics: "external" and has one. | Add the create handler, or remove it from a Grants-only connector. |
grant.<kind>.add | MISSING_METHOD | A writable Grant kind has no add handler. | Add handlers/grants/<kind>/add.ts, or make the Grant kind read-only. |
grant.<kind>.remove | MISSING_METHOD | A writable Grant kind has no remove handler. | Add handlers/grants/<kind>/remove.ts. |
artifact.notification.send | MISSING_METHOD | provides includes notification but there is no notification.send. | Add handlers/notification/send.ts, or remove notification from provides. |
artifact.<handler> | INVALID_METHOD | A handler file names something the manifest does not declare (an entity, a Grant kind, a custom operation), is not a handler path, needs a capability the manifest lacks, or does not export a function. | Declare what it names, move the file to a handler path, or delete it. |
handler.unused.<handler> | INVALID_METHOD | A handler can never run, for example a Grant handler for a read-only Grant kind, or a custom-operation handler on an entity that is not the account. | Delete it, or make the Grant kind writable. |
types.<TypeName>.collision | INVALID_GRAPH | Two keys (such as user-group and user_group) generate the same type name. | Rename one of them. |
Entities and the account
| Requirement | Code | Means | Fix |
|---|---|---|---|
connector.account_entity | MISSING_ENTITY, INVALID_ENTITY | The connector provides provision but has no entity with is_account: true, or has more than one. | Mark exactly one entity as the account. |
entity.<entity>.reserved | INVALID_ENTITY | The entity key is grant, grants, connection or notification. | Choose another key. |
entity.syncable | MISSING_ENTITY | The connector provides sync but declares no entities. | Declare the entities, or remove sync. |
entity.<entity>.invite.reinvite_op | INVALID_ENTITY | The account's invite.reinvite_op names a custom operation that custom_ops does not declare. | Declare the operation in custom_ops, or correct the name. |
entity.<entity>.create_semantics | INVALID_ENTITY | The account declares invite or identifier_transition: true, but its create_semantics is not invite. | Set create_semantics to invite, or remove the invitation settings. |
entity.<entity>.provisioning_profile.operations.PROVISION.provision_mode | INVALID_ENTITY | The account's provision_mode does not match its create_semantics: direct (the default) needs direct, invite needs invite, and external needs external. | Make the two match, or leave provision_mode out. |
entities.<entity>.sync_profile.is_active | INVALID_CONFIG | The account's sync_profile has target RESOURCE or HYBRID but no is_active, so Sync cannot tell an active account from a deactivated one. | Add an is_active expression, such as status != 'deactivated'. |
entities.<entity>.sync_profile.<path>.expression | INVALID_ENTITY | A sync_profile expression (is_active, is_deleted, is_person, a matching rule's or an attribute mapping's externalExpression) is not a valid expression. <path> is the field, such as matchingRules[1].externalExpression. | Fix the syntax; see Expressions. |
entities.<entity>.provisioning_profile.<path>.expression | INVALID_ENTITY | A baseline attribute { "type": "expression", "value": "..." } is not a valid expression. <path> is the attribute, such as operations.PROVISION.baseline.attributes.mail. | Fix the syntax; see Expressions. |
entity.<entity>.schema.x-reference | INVALID_ENTITY_SCHEMA | A property's x-reference is malformed, sits on a nested property, or does not point at the account entity and one of its readable, top-level string properties (or externalId). The message names the property. | Set entity to the account entity and attribute to a top-level string property that is not writeOnly or x-sensitive, or to externalId. |
entity.<entity>.<path> | INVALID_ENTITY_SCHEMA | An x-provider-stable-id annotation at the schema path that follows (such as schema.properties.id) is not true or false, is not on a top-level identifier property, or is on a writeOnly or x-sensitive property; or the schema uses a near spelling of it, such as x-provider-stable-idd. | Put "x-provider-stable-id": true only on a readable, top-level identifier property, spelled exactly. |
entity.<entity>.<path> | INVALID_ENTITY_SCHEMA | A writeOnly or x-sensitive flag sits at the schema path that follows where no attribute path reaches it (array items, additionalProperties, patternProperties), or, on the account, at or below a property whose name holds a dot. Owlie protects secret values by their path, so this one could never be protected. | Move the flag onto a property Owlie reaches by name, and keep dots out of the property names above it. |
entity.<entity>.identifier_paths.<n> | INVALID_ENTITY_SCHEMA | An account identifier is a credential (both writeOnly and x-sensitive). Identifiers are recorded and reused to find the account. | Remove the credential from identifier_paths. |
entity.<entity>.provisioning_profile.password | INVALID_ENTITY | The account's provisioning profile has a password block, but its create_semantics is external, so Owlie never creates its accounts. | Remove the password block. |
entity.<entity>.provisioning_profile.password.<field> | INVALID_ENTITY | A field of the account's password block does not fit the account schema. <field> is paths.<n> (not a declared string credential writable at creation, or, with link or self_service, not changeable afterwards), initial_length or policy.min_length (outside a path's minLength or maxLength, or shorter than the policy minimum), on_change.<path> (undeclared, not changeable by a password change, sensitive, an identifier, overlapping a password path, of the wrong type, or set by a baseline mapping that an attribute update would revert), or history_check (other than person_changes while a path's property lacks x-provisioning.passwordHistory: true). | Correct the field the message names. |
entity.<entity>.schema.x-scope | INVALID_ENTITY_SCHEMA | An x-scope is malformed or misplaced: not { entity } with a declared entity, on a nested or non-string property, on an entity no Grant kind targets, on a writeOnly or x-sensitive property, left out of sync_profile.fetchOptions.fields, or on more than one property of the entity. The message names the property. | Put one x-scope on a readable, top-level string property of the Grant target, naming a declared entity, and make sure Sync fetches it. |
Grant kinds
| Requirement | Code | Means | Fix |
|---|---|---|---|
grant_kind.<kind>.unique | INVALID_GRAPH | Two Grant kinds share a key. | Rename one. |
grant_kind.<kind>.charset | INVALID_GRAPH | The key does not start with a letter, or has characters other than letters, digits, _ and -. | Rename it; the key names a folder under handlers/grants/. |
grant_kind.<kind>.source | INVALID_GRAPH | source names an entity that is not declared. | Fix source or declare the entity. |
grant_kind.<kind>.target | INVALID_GRAPH | target names an entity that is not declared. | Fix target or declare the entity. |
grant_kind.<kind>.discovered_via | INVALID_GRAPH | discovered_via names an entity that is not declared. | Fix it or declare the entity. |
grant_kind.<kind>.principal_types | INVALID_GRAPH | A principal type is not a declared entity, or the list leaves out source. | Declare the entity; include source. |
grant_kind.<kind>.writable_capability | INVALID_GRAPH | The Grant kind is writable but provides lacks provision. | Add provision, or make the Grant kind read-only. |
grant_kind.<kind>.derived_readonly | INVALID_GRAPH | A derived Grant kind is marked writable. | Remove writable. |
grant_kind.<kind>.qualifier_names | INVALID_GRAPH | Two qualifiers share a name. | Rename one. |
grant_kind.<kind>.qualifiers | INVALID_GRAPH | A qualifier is both slot_key and multi_valued; an entity_ref lacks entity; entity names an undeclared entity; or a non-entity_ref qualifier has entity. | Correct the qualifier. |
grant_kind.<kind>.enum_values | INVALID_GRAPH | An enum's values, order or default do not agree, or a non-enum qualifier has them. | order must list exactly the values; default must be one of them. |
grant_kind.<kind>.qualifier_pattern | INVALID_GRAPH | The pattern is not a valid regular expression, could take exponential time to match, or is on a qualifier that cannot have one. | Simplify the pattern (no repeated group that itself repeats something, such as (a+)+, no repeated group of alternatives, and no backreferences); use it only on string and entity_ref. |
grant_kind.<kind>.qualifier_examples | INVALID_GRAPH | An example does not match the pattern or values, or the qualifier type takes no examples. | Fix or remove the example. |
grant_kind.<kind>.qualifier_source | INVALID_GRAPH | A qualifier's source does not start with attrs.. | Use attrs.<name>. |
grant_kind.<kind>.validator_refs | INVALID_GRAPH | A validator names qualifiers that do not exist or have the wrong type. | Point it at qualifiers of the right type. |
grant_kind.<kind>.membership_context | INVALID_GRAPH | membership_context is set without membership: true, repeats a name, or names something other than one qualifier of this Grant kind that is required, not multi_valued, and has a single value (not set or record; an entity_ref must declare entity). | Declare each named qualifier once, with required: true and a single value. |
grant_kind.<kind>.prerequisite | INVALID_GRAPH | The Grant kind's prerequisite cannot hold: target_from_qualifier is not a required, single-valued entity_ref qualifier of this kind; kind is undeclared, this kind itself, not writable, targets another entity than the qualifier's, or misses one of this kind's principal types; a required qualifier of kind without a default is not declared here with the same name, schema and entity; or the chain of prerequisites loops. | Correct the prerequisite as the message says. |
Authentication
| Requirement | Code | Means | Fix |
|---|---|---|---|
auth.<option>.duplicate | INVALID_AUTH | Two authentication options share a key. | Rename one. |
auth.<option>.config.non_overlapping_shared | INVALID_CONFIG | The option's own config declares a field that the connector's shared config also declares, so one install value would answer both. | Rename one of them, or keep the field in one place. |
auth.connect.callback_validation | INVALID_AUTH | An option's connect uses callback_validation with intuit_realm but marks credentials or refresh { "custom": true }. Owlie saves the realm only through its own credential handling. The message names the option. | Use the built-in credentials and refresh stages on that option. |
auth.<option>.<stage>.<field>.literal_secret | INVALID_METHOD | A secret field is written into the manifest as a literal, or its expression reads nothing. | Read it from a configuration property marked secret: { "expr": "config.client_secret" }. |
auth.<option>.<stage>.<field>.insensitive_reference | INVALID_METHOD | A secret field reads a configuration property that is not declared, or not marked secret. | Declare the property with "format": "password" and "x-sensitive": true. |
auth.<option>.<path>.expression | INVALID_AUTH | An { "expr": "..." } value in an authentication option, or its enabled expression, is not a valid expression, or reads a root authentication expressions cannot. The message gives the parser's explanation and the line and column. <path> is the field, such as acquire.oauth2_client_credentials.token_url. | Fix the syntax; see Expressions. |
auth.<option>.<hook> | MISSING_METHOD, INVALID_METHOD | A stage is marked { "custom": true } but lib/auth-hooks.ts has no matching function; or the file has a function no custom stage uses, or that is not a function. A custom refresh also needs prepareRefresh. | Add or remove the hook so the two match. |
auth.<option>.reject.error | INVALID_HTTP_RATE_LIMIT_POLICY | The option's reject rule has a pointer that is more than 8 segments deep or badly escaped (~ must be ~0 or ~1), has more than one * segment, or has a * segment with no match. | Shorten or fix the pointer, use at most one *, and set match to "all" or "any" when there is one. See Credential rejection. |
auth.<option> | INVALID_METHOD | lib/auth-hooks.ts has hooks for an option the manifest does not declare, or its entry is not an object. | Match the option key, or remove the entry. |
definition.hooks.auth | INVALID_METHOD | The exported authHooks is not keyed by authentication option. | Export an object keyed by option key. |
Placement and rate limits
| Requirement | Code | Means | Fix |
|---|---|---|---|
execution_targets.gateway_only_for_ldap_sql | INVALID_EXECUTION_TARGETS | A connector that needs LDAP or SQL allows cloud execution. | Set execution_targets to ["gateway"], or leave it out. |
http_rate_limits.schema | INVALID_HTTP_RATE_LIMIT_POLICY | The rate-limit declaration does not match its schema. | Check it against Connector manifest. |
http_rate_limits.buckets.<n>.key.unique | INVALID_HTTP_RATE_LIMIT_POLICY | Two buckets share a key. | Rename one. |
http_rate_limits.buckets.<n>.key.reserved | INVALID_HTTP_RATE_LIMIT_POLICY | A bucket uses the reserved key integration-default. | Choose another key. |
http_rate_limits.buckets.<n>.feedback.headers | INVALID_HTTP_RATE_LIMIT_POLICY | A feedback header names one of Owlie's own coordination headers. | Use the vendor's rate-limit headers. |
http_rate_limits.buckets.<n>.feedback.body | INVALID_HTTP_RATE_LIMIT_POLICY | A JSON pointer is too deep or badly escaped. | Fix the pointer. |
http_rate_limits.rules.<n>.buckets.reference | INVALID_HTTP_RATE_LIMIT_POLICY | A rule names a bucket that does not exist. | Declare the bucket or fix the name. |
http_rate_limits.rules.<n>.buckets.unique | INVALID_HTTP_RATE_LIMIT_POLICY | A rule names the same bucket twice. | List it once. |
http_rate_limits.rules.<n>.origin.literal_https | INVALID_HTTP_RATE_LIMIT_POLICY | A rule's origin is not a plain HTTPS origin. | Use https://host with no path, query, fragment or credentials. |
http_rate_limits.rules.<n>.origin.egress_allowed | INVALID_HTTP_RATE_LIMIT_POLICY | A rule's origin is not allowed by the connector's egress. | Add the domain to egress, or fix the origin. |
http_rate_limits.rules.<n>.path.segment_glob | INVALID_HTTP_RATE_LIMIT_POLICY | A rule's path uses a pattern other than whole segments. | Use literal segments, * for one segment, and a final **. |
http_rate_limits.quota.dimensions | INVALID_HTTP_RATE_LIMIT_POLICY | A quota dimension is not a distinct, non-secret scalar configuration field or a supported binding field. | Point each dimension at such a field. |
The pack, files and manifest
| Requirement | Code | Means | Fix |
|---|---|---|---|
pack<path> | INVALID_PACK_SHAPE, INVALID_CONNECTOR_METADATA | The pack document, or a part of it named by the path that follows (such as pack/connector/key), has the wrong shape. | Compare with Pack format. |
manifest<path> | INVALID_MANIFEST | The manifest, or the part named by the path that follows (such as manifest/entities/user), does not match the manifest schema. | Compare with Connector manifest. |
manifest/<path> | FORBIDDEN_KEY | A manifest key is __proto__, constructor or prototype. | Rename the key. |
manifest | MANIFEST_MISMATCH, PACK_TOO_LARGE | For LDAP, SCIM, SQL and flat-file connectors: the pack's manifest is not what the engine derives from its definition.json. Or: the manifest is over 512 KiB. | Put the derived manifest in the pack: copy it from the connector's Derived manifest tab, or export the connector again. Never edit a derived manifest by hand. Reduce the manifest's size. |
connector.key, connector.version, connector.name | MANIFEST_MISMATCH | The pack's connector metadata and the manifest disagree. | Make them equal. |
connector.kind | MANIFEST_MISMATCH, KIND_MISMATCH | The pack's kind differs from the manifest's, or from the kind of the connector you are importing into. | Import into a connector of the same kind; the kind never changes. |
files | PACK_TOO_LARGE | More than 512 files, or more than 4 MiB of files. | Remove files. |
files.<path> | INVALID_PATH, UNSAFE_SCRIPT, PACK_TOO_LARGE, FORBIDDEN_KEY | A file is outside the allowed locations or extensions, is not a handler path under handlers/, is over 512 KiB, or uses a reserved name. UNSAFE_SCRIPT: a PowerShell script evaluates text or runs what it does not name (any use of Invoke-Expression or iex, [ScriptBlock]::Create, .NewScriptBlock(, .InvokeScript(, $ExecutionContext InvokeCommand, [PowerShell]::Create, .AddScript(, Add-Type, & or . before a variable, expression or string, dot-sourcing a file, starting pwsh, powershell or cmd /c, Start-Process, Invoke-Command or Invoke-Item), or starts with param(...), using or #requires. Comments and single-quoted text are not checked. | Move, rename, shrink or rewrite the file (Pack format). A script reads its parameters from $Params. |
files.definition.json | INVALID_DEFINITION | An HTTP connector has a definition.json, another kind lacks one, or it is not valid JSON. | Add, remove or fix definition.json. |
files.definition.json#<path> | INVALID_DEFINITION | The definition is invalid at that path; the message says why. | Fix the field (definition references). |
definition | ValidationError | The definition is missing, is not JSON, does not derive, or the engine cannot run it. | Fix definition.json; the message carries the engine's reason. |
definition.<field> | INVALID_METHOD | The connector Owlie assembles from your files has an unknown top-level key. Owlie sets these keys itself, so a connector made of documented files does not meet this. | Contact Owlie support with the message. |
definition.hooks.<hook> | INVALID_METHOD | A lib/ hook file does not export its hook as a function, or exports one Owlie does not know. | Export only mapError, checkResponse, classifyResponse or authHooks, each as documented. |
engine.handler | ValidationError | A test named a handler this definition does not enable. | Test a handler the definition enables. |
engine.override | ValidationError | A file under handlers/ replaces a handler the definition does not enable. | Enable the operation in the definition, or delete the override. |
engine.derive, engine.runtime | ValidationError | This kind's engine cannot derive or run connectors yet. | Choose a kind that is available in the builder. |
Publishing also compiles your files. A publish whose files do not compile is refused with
ValidationError and a message that starts "Connector compilation failed", followed by each error;
through GraphQL each one is also in extensions.context.build_errors, with the handler it belongs to.
Creating, importing and forking
| Requirement | Code | Means | Fix |
|---|---|---|---|
custom_connector.key_pattern | ValidationError | The key has characters other than lowercase letters, digits, - and _, or does not start with a letter or digit. | Change the key. |
custom_connector.key_taken, target.create.key | ValidationError, KEY_TAKEN | A connector with this key exists, or existed and was deleted. Keys are never reused. | Choose another key. |
custom_connector.kind | ValidationError | The kind is not http, scim, ldap, sql or flat-file. | Use one of those. |
custom_connector.import.target | ValidationError | An import sets both or neither of target.create and target.replace_draft. | Set exactly one. |
custom_connector.fork.base | ValidationError | A fork sets both or neither of base.system and base.tenant. | Set exactly one. |
custom_connector.fork.not_forkable | ValidationError, CUSTOM_CONNECTOR_NOT_FORKABLE | The connector connects only through Owlie's own app registration, so a fork (or a duplicate) could never sign in. | Install the Owlie connector instead; duplicating it is refused for the same reason. |
custom_connector.fork.not_a_fork | ValidationError | A fork action was called on a connector that is not a fork. | Call it on a fork. |
custom_connector.fork.upstream_conflicts | ValidationError, FORK_UPSTREAM_CONFLICT | The base changed something the fork also changed. | Resolve each conflict (keep yours or take upstream) before publishing or detaching. |
custom_connector.fork.conflict_path | ValidationError | A resolution names a path that has no conflict. | Resolve only the paths the fork lists. |
custom_connector.fork.keep_mine_container_removed | ValidationError | The base removed what the path lives in, so your version has nowhere to stay. | Take upstream, or edit the file. |
custom_connector.fork.revert_path | ValidationError | A revert names a path the fork does not change. | Revert only paths the fork changes. |
Revision conflicts are reported as errors rather than issues: CUSTOM_CONNECTOR_REVISION_CONFLICT
when the draft changed since you read it, and CUSTOM_CONNECTOR_BASE_CHANGED when a fork's base moved.
Read the draft again and retry.
Test runs
| Requirement | Code | Means | Fix |
|---|---|---|---|
draft.allow-writes | ValidationError | A draft test on a gateway (LDAP, SQL, or an HTTP connector that runs only on a gateway) would run a handler that can change the system, and the run did not confirm it. On SQL that is every handler except connection.validateConfig and connection.validateStatements. Tests in Owlie's cloud never ask. | Confirm with Run it in the builder, or send "allow_writes": true. |
draft.gateway | ValidationError | The connector runs on a gateway and none is connected, more than one is and none was chosen, or the chosen one is not active. | Connect a gateway, or pass gateway_id. |
draft.gateway-allowlist | ValidationError | The gateway's operator has not approved this connector. | The operator adds the connector's id to the gateway's approved custom connectors (private-network gateways). |
draft.secret-ref | ValidationError | A draft test on a gateway was given a gateway secret name instead of a typed credential. | Type the test credential in Run config. |
custom_connector.test.http_uses_run_test | ValidationError | connectors.testArtifact was called for the draft of an HTTP connector that runs in Owlie's cloud. Test it with connectors.runTest (Test your connector). | |
custom_connector.test.http_only | ValidationError | connectors.runTest was called for a connector that is not an HTTP connector, or for one limited to a gateway, which cannot run in Owlie's cloud. | |
custom_connector.test.draft_only | ValidationError | connectors.runTest was called for a published version. Only a draft can be tested. | |
custom_connector.test_hosts.too_many | ValidationError | A test run would reach more than 20 hosts. Declare fewer hosts, or remove entries from egress_domains in the saved Run config. |