Troubleshooting Guide
February 24, 2026 · View on GitHub
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
- Connection Problems
- Authentication & Signing Issues
- Event Publishing Issues
- Subscription Issues
- Encryption & Decryption Issues
- Performance Issues
- Integration Testing Issues
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.xyzwss://nos.lolwss://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:
- Docker not available: Skip tests when Docker is unavailable:
@DisabledIfSystemProperty(named = "noDocker", matches = "true")
- Resource constraints: Use tmpfs for relay storage:
.withTmpFs(Map.of("/app/strfry-db", "rw"))
Getting More Help
If your issue isn't covered here:
- Check the API reference: reference/nostr-java-api.md
- Review examples: howto/api-examples.md
- Search existing issues: GitHub Issues
- 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