Skip to main content
Something broke? Start here. Most connector issues fall into recognizable patterns.

Debugging workflow

When a connector fails, follow this diagnostic sequence:
  1. Check logs - What error message?
  2. Inspect output - Did sync produce a .c1z file?
  3. Verify config - Are credentials valid?
  4. Test API access - Can you hit the target API directly?
  5. Isolate the issue - Which resource type fails?

Enable debug logging

./baton-yourservice --log-level debug --log-format json
Log levels: debug, info, warn, error

Inspect sync output

Use the baton CLI to inspect what was synced:
baton resources -f sync.c1z    # List all resources
baton grants -f sync.c1z       # List grants
baton entitlements -f sync.c1z # List entitlements
baton stats -f sync.c1z        # Get stats

Common errors

Pagination loop detected

Error: next page token is the same as the current page token Cause: Your List(), Entitlements(), or Grants() method returned the same pagination token it received.
// BAD - Returns same token, causes infinite loop
func (u *userBuilder) List(ctx context.Context, parentID *v2.ResourceId,
    pToken *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {

    users, err := u.client.GetUsers(ctx)
    return resources, pToken.Token, nil, nil  // WRONG - returning input token
}

// GOOD - Returns next page token from API, or empty when done
func (u *userBuilder) List(ctx context.Context, parentID *v2.ResourceId,
    pToken *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {

    users, nextPage, err := u.client.GetUsers(ctx, pToken.Token)
    return resources, nextPage, nil, nil  // CORRECT
}
The SDK detects this and returns an error to prevent infinite loops.

Authentication failed

Error: 401 Unauthorized or 403 Forbidden Causes:
  • Invalid or expired credentials
  • Missing required scopes/permissions
  • Wrong API endpoint
Diagnostic steps:
# Test credentials directly
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/v1/users

# Check token expiration (for JWT tokens, decode and check 'exp' claim)

# Verify required scopes in target API documentation
IssueFix
Expired tokenRefresh or regenerate credentials
Missing scopeRequest additional API permissions
Wrong endpointCheck --base-url or environment config

Rate limited

Error: 429 Too Many Requests or slow sync performance Solutions:
  1. Reduce page size (more pages, smaller requests):
    users, next, err := client.ListUsers(ctx, pageSize: 100)
    
  2. The SDK handles backoff automatically via uhttp:
    httpClient, _ := uhttp.NewBaseHttpClient(ctx)
    // Automatic exponential backoff on 429
    
  3. Add explicit delays if needed:
    time.Sleep(time.Second) // Between API calls
    

Resource type mismatch

Error: Grants reference principals that don’t exist Cause: The ResourceId in a grant doesn’t match any synced resource.
// BAD - Type ID doesn't match
g := grant.NewGrant(resource, "member",
    &v2.ResourceId{ResourceType: "User", Resource: member.ID})  // "User" vs "user"

// GOOD - Type ID matches exactly
g := grant.NewGrant(resource, "member",
    &v2.ResourceId{ResourceType: "user", Resource: member.ID})  // Matches userBuilder
Debug:
baton resources -f sync.c1z | grep -i "resource_type"
baton grants -f sync.c1z | grep "principal"

Empty sync results

Error: Sync completes but produces no resources Causes:
  • API returns empty results (permissions issue)
  • Pagination starts past end of data
  • Filter too restrictive
baton stats -f sync.c1z
./baton-yourservice --log-level debug 2>&1 | grep -i "response\|count"
SymptomLikely causeFix
0 usersMissing read permissionCheck API credentials scope
0 grantsNo memberships existVerify data exists in target system
Partial dataPagination bugCheck token handling

Connection refused

Error: connection refused or no such host
curl -v https://api.example.com/health  # Test connectivity
nslookup api.example.com                # Check DNS
./baton-yourservice --base-url https://internal.example.com

TLS certificate errors

Error: x509: certificate signed by unknown authority For testing only - skip TLS verification:
./baton-yourservice --insecure-skip-verify
Never use --insecure-skip-verify in production.
For production - add the CA certificate:
export SSL_CERT_FILE=/path/to/ca-bundle.crt

Performance issues

Slow sync

CauseDiagnosisFix
Too many API callsDebug logs show thousands of requestsBatch where possible
No paginationSingle huge requestImplement proper paging
N+1 queriesFetching details one-by-oneUse bulk endpoints
Rate limiting429 errors in logsReduce page size
// BAD - N+1 pattern (one API call per user)
for _, user := range users {
    details, _ := client.GetUserDetails(ctx, user.ID)
}

// GOOD - Bulk fetch
details, _ := client.GetUsersWithDetails(ctx, userIDs)

Memory issues

CauseFix
Loading all data in memoryUse pagination, stream processing
Large responses not garbage collectedProcess in batches
Caching too muchLimit cache size, use LRU

Daemon mode issues

Task polling failures

Error: failed to poll for tasks or authentication error
./baton-yourservice \
    --client-id $CLIENT_ID \
    --client-secret $CLIENT_SECRET \
    --log-level debug

Connector not receiving tasks

Symptoms: Daemon runs but never processes anything Check:
  • Connector registered in ConductorOne admin UI
  • Sync scheduled
  • Correct connector ID

Provisioning issues

Grant/revoke failures

Error: error: provisioner not found for resource type Cause: Declared provisioning capability but didn’t implement the interface.
// Implement ResourceProvisionerV2 interface
func (g *groupBuilder) Grant(ctx context.Context,
    principal *v2.Resource, entitlement *v2.Entitlement) (
    []*v2.Grant, annotations.Annotations, error) {
    // Implementation
}

func (g *groupBuilder) Revoke(ctx context.Context,
    grant *v2.Grant) (annotations.Annotations, error) {
    // Implementation
}
Verify capabilities:
./baton-yourservice capabilities | jq '.resourceTypeCapabilities[] | select(.capabilities | contains(["CAPABILITY_PROVISION"]))'

Duplicate objects after sync

Symptom

After running a sync, you see duplicate resources in ConductorOne: one created via Terraform/API and a separate one discovered by the connector.

Causes

  1. RawId not set - Connector doesn’t include the RawId annotation
  2. ID format mismatch - Connector uses a different ID format than expected
  3. match_baton_id value incorrect - Terraform resource has wrong ID value
  4. Case sensitivity - IDs may be case-sensitive

Resolution

Step 1: Verify the connector outputs RawId annotation:
./baton-yourservice --log-level debug 2>&1 | grep -i rawid
Step 2: Check what ID the connector uses:
ConnectorID FieldExample
Oktaapp.Id0oa1xyz789abcdef0h7
Azure ADObject ID12345678-1234-...
GCPResource pathprojects/my-project
AWSFull ARNarn:aws:iam::123...
Step 3: Update Terraform to use the exact match:
resource "conductorone_app" "example" {
  display_name   = "My App"
  match_baton_id = "0oa1xyz789abcdef0h7"  # Exact value from connector
}
Step 4: Delete duplicate and re-sync.

Diagnostic commands

# Quick health check
make build && ./dist/*/baton-yourservice \
    --log-level debug \
    --log-format json \
    2>&1 | head -100

# Inspect .c1z file
baton stats -f sync.c1z
baton resources -f sync.c1z --resource-type user
baton resources -f sync.c1z --output-format json > resources.json

# Compare syncs
./baton-yourservice && mv sync.c1z sync1.c1z
./baton-yourservice && mv sync.c1z sync2.c1z
baton stats -f sync1.c1z
baton stats -f sync2.c1z

Getting help

If you’ve tried these steps and still have issues:
  1. Search existing issues on the connector’s GitHub repo
  2. Check SDK issues at baton-sdk issues
  3. Open a new issue with:
    • Connector name and version
    • Error message (full text)
    • Debug logs (sanitized of credentials)
    • Steps to reproduce

Quick reference

Debug flags

FlagPurpose
--log-level debugVerbose logging
--log-format jsonStructured logs for parsing
--insecure-skip-verifySkip TLS verification (testing only)
--base-urlOverride API endpoint

Diagnostic tools

ToolCommandPurpose
batonbaton resources -f sync.c1zInspect sync output
batonbaton stats -f sync.c1zSummary statistics
curlcurl -v $API_URLTest API connectivity
jqcat sync.json | jq '.'Parse JSON output