Skip to content

Latest commit

 

History

History
458 lines (327 loc) · 10.5 KB

File metadata and controls

458 lines (327 loc) · 10.5 KB

Troubleshooting Guide

Navigation: Docs index · Getting started · API reference

This guide helps you diagnose and resolve common issues when using nostr-java.

Table of Contents


Installation Issues

Problem: Dependency Not Found

Symptom: Maven or Gradle cannot resolve xyz.tcheeric:nostr-java-client:<version>

Solution: Ensure you've added the custom repository to your build configuration:

Maven:

<repositories>
  <repository>
    <id>nostr-java</id>
    <url>https://maven.398ja.xyz/releases</url>
  </repository>
</repositories>

Gradle:

repositories {
    maven { url 'https://maven.398ja.xyz/releases' }
}

Problem: Java Version Mismatch

Symptom: UnsupportedClassVersionError or compilation errors

Solution: nostr-java requires Java 21 or higher. Verify your Java version:

java -version

If needed, update your build configuration:

Maven (pom.xml):

<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
</properties>

Gradle (build.gradle):

java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}

Problem: Conflicting Dependencies

Symptom: ClassNotFoundException or NoSuchMethodError at runtime

Solution: Check for dependency conflicts, especially with Spring WebSocket or JSON libraries:

# Maven
mvn dependency:tree

# Gradle
gradle dependencies

Exclude conflicting transitive dependencies if needed (version managed by the BOM):

<dependency>
    <groupId>xyz.tcheeric</groupId>
    <artifactId>nostr-java-client</artifactId>
    <exclusions>
        <exclusion>
            <groupId>conflicting-group</groupId>
            <artifactId>conflicting-artifact</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Connection Problems

Problem: WebSocket Connection Fails

Symptom: IOException, ConnectException, or timeouts when connecting to relay

Possible Causes & Solutions:

1. Invalid Relay URL

Ensure the relay URL uses the correct WebSocket protocol:

  • Use wss:// for secure connections (recommended)
  • Use ws:// only for local development (e.g., ws://localhost:5555)

Bad:

new NostrRelayClient("https://relay.398ja.xyz");  // Wrong protocol

Good:

new NostrRelayClient("wss://relay.398ja.xyz");

2. Relay is Down or Unreachable

Test the relay URL independently:

# Using websocat (install: cargo install websocat)
websocat wss://relay.398ja.xyz

# Or use an online WebSocket tester

Try alternative public relays:

  • wss://relay.398ja.xyz
  • wss://nos.lol
  • wss://relay.nostr.band

3. Firewall or Proxy Blocking WebSocket

If behind a corporate firewall, configure proxy settings:

System.setProperty("https.proxyHost", "proxy.example.com");
System.setProperty("https.proxyPort", "8080");

Problem: Connection Drops Unexpectedly

Symptom: Subscription stops receiving events after a period

Solution: Use retry and error handling with NostrRelayClient:

try (NostrRelayClient client = new NostrRelayClient("wss://relay.398ja.xyz")) {
    AutoCloseable subscription = client.subscribe(
        req,
        message -> System.out.println(message),
        error -> {
            System.err.println("Connection error: " + error.getMessage());
            // NostrRelayClient uses Spring Retry with exponential backoff
        },
        () -> System.out.println("Connection closed")
    );
}

Authentication & Signing Issues

Problem: Event Signature Verification Fails

Symptom: Relay rejects event with signature error

Possible Causes:

1. Event Not Signed

Ensure you sign the event before sending:

Bad:

GenericEvent event = GenericEvent.builder()
    .pubKey(identity.getPublicKey())
    .kind(Kinds.TEXT_NOTE)
    .content("Hello")
    .build();
client.send(new EventMessage(event));  // Missing signature!

Good:

GenericEvent event = GenericEvent.builder()
    .pubKey(identity.getPublicKey())
    .kind(Kinds.TEXT_NOTE)
    .content("Hello")
    .build();
identity.sign(event);  // Sign first
client.send(new EventMessage(event));

2. Event Modified After Signing

Never modify an event after signing it. Any change invalidates the signature.

3. Incorrect Key Format

Ensure private keys are in the correct format (32-byte hex string, 64 hex characters):

String validKey = "a".repeat(64);
Identity id = Identity.create(validKey);

Event Publishing Issues

Problem: Events Not Appearing on Relay

Debugging Steps:

1. Verify Event Structure

GenericEvent event = GenericEvent.builder()
    .pubKey(identity.getPublicKey())
    .kind(Kinds.TEXT_NOTE)
    .content("Hello")
    .build();
identity.sign(event);

// Log the event JSON
String json = new EventMessage(event).encode();
System.out.println("Sending event: " + json);

2. Check Relay Response

List<String> responses = client.send(new EventMessage(event));
responses.forEach(response ->
    System.out.println("Relay response: " + response)
);

Problem: Relay Timeout

Symptom: RelayTimeoutException when sending events

Solution: Increase the timeout or check relay connectivity:

// Increase timeout to 2 minutes
NostrRelayClient client = new NostrRelayClient("wss://relay.398ja.xyz", 120_000);

Or configure via Spring properties:

nostr.websocket.await-timeout-ms=120000

Subscription Issues

Problem: Subscription Receives No Events

Debugging Steps:

1. Verify Filter Configuration

// Too restrictive — might match nothing
EventFilter tooRestrictive = EventFilter.builder()
    .authors(List.of(specificPubKey))
    .kinds(List.of(Kinds.TEXT_NOTE))
    .since(Instant.now().getEpochSecond())  // Only future events
    .build();

// More permissive — should match events
EventFilter permissive = EventFilter.builder()
    .kinds(List.of(Kinds.TEXT_NOTE))
    .limit(10)
    .build();

2. Test with Basic Filters

// Most widely supported
EventFilter basic = EventFilter.builder()
    .kinds(List.of(Kinds.TEXT_NOTE))
    .limit(5)
    .build();

Problem: Subscription Callback Blocks

Symptom: Application becomes unresponsive or slow

Note: In nostr-java 2.0, subscription callbacks are dispatched on Virtual Threads, so blocking in callbacks does not block WebSocket I/O. However, for high-throughput feeds, consider using a bounded queue:

BlockingQueue<String> eventQueue = new LinkedBlockingQueue<>(1000);

client.subscribe(req,
    message -> {
        if (!eventQueue.offer(message)) {
            System.err.println("Queue full, dropping event");
        }
    },
    error -> System.err.println(error),
    () -> {}
);

// Process from queue at controlled rate
while (running) {
    String message = eventQueue.poll(1, TimeUnit.SECONDS);
    if (message != null) processMessage(message);
}

Encryption & Decryption Issues

Problem: Decryption Fails

Symptom: NostrException or garbled plaintext

Possible Causes:

1. Wrong Private Key

Ensure you're using the recipient's private key to decrypt:

Identity alice = Identity.generateRandomIdentity();
Identity bob = Identity.generateRandomIdentity();

// Alice encrypts for Bob
MessageCipher04 cipher = new MessageCipher04(alice.getPrivateKey(), bob.getPublicKey());
String encrypted = cipher.encrypt("Secret message");

// Bob decrypts (using his private key + Alice's public key)
MessageCipher04 bobCipher = new MessageCipher04(bob.getPrivateKey(), alice.getPublicKey());
String decrypted = bobCipher.decrypt(encrypted);

Problem: NIP-44 vs NIP-04 Confusion

Solution: Match encryption and decryption versions:

// NIP-04 (legacy)
MessageCipher04 cipher04 = new MessageCipher04(senderPriv, recipientPub);
String encrypted04 = cipher04.encrypt("Hello");

// NIP-44 (recommended)
MessageCipher44 cipher44 = new MessageCipher44(senderPriv, recipientPub);
String encrypted44 = cipher44.encrypt("Hello");

// Can't mix: cipher04.decrypt(encrypted44) will fail!

Performance Issues

Problem: Slow Event Publishing

Solutions:

Use Async APIs

NostrRelayClient.connectAsync("wss://relay.398ja.xyz")
    .thenCompose(client -> client.sendAsync(new EventMessage(event)))
    .thenAccept(responses -> System.out.println("Done: " + responses));

Problem: High Memory Usage

Solutions:

1. Limit Subscription Results

EventFilter filter = EventFilter.builder()
    .kinds(List.of(Kinds.TEXT_NOTE))
    .limit(100)
    .build();

2. Close Subscriptions When Done

AutoCloseable subscription = client.subscribe(/* ... */);
try {
    // Use subscription
} finally {
    subscription.close();  // Always close!
}

Integration Testing Issues

Problem: Tests Timeout After 60 Seconds

Solution: Use strfry relay instead of nostr-rs-relay:

# src/test/resources/relay-container.properties
relay.container.image=dockurr/strfry:latest
relay.container.port=7777

Problem: Tests Fail in CI but Pass Locally

Possible Causes:

  1. Docker not available: Skip tests when Docker is unavailable:
@DisabledIfSystemProperty(named = "noDocker", matches = "true")
  1. Resource constraints: Use tmpfs for relay storage:
.withTmpFs(Map.of("/app/strfry-db", "rw"))

Getting More Help

If your issue isn't covered here:

  1. Check the API reference: reference/nostr-java-api.md
  2. Review examples: howto/api-examples.md
  3. Search existing issues: GitHub Issues
  4. Open a new issue: Provide nostr-java version, Java version, minimal reproducing code, and full stack trace.

Debug Logging

For Spring Boot applications, add to application.properties:

logging.level.nostr=DEBUG
logging.level.nostr.client=TRACE