+15
.claude/commands/check-spans.md
+15
.claude/commands/check-spans.md
···
···
1
+
---
2
+
description: Investigate Logfire spans and traces to answer questions about application behavior
3
+
argument-hint: [your question about what happened]
4
+
---
5
+
6
+
First read the Logfire documentation: @docs/tools/logfire.md
7
+
8
+
Investigate: $ARGUMENTS
9
+
10
+
Query the `records` table using SQL patterns from the docs. Key points:
11
+
- Search by `message` for logfire.info() events, `span_name` for spans
12
+
- Use `trace_id` to follow a full user action flow
13
+
- Check `otel_status_message` for errors (not exception.message)
14
+
- Filter by `start_timestamp` for time ranges
15
+
- Extract metadata from `attributes` JSONB field
+36
-5
src/backend/api/tracks/uploads.py
+36
-5
src/backend/api/tracks/uploads.py
···
221
except json.JSONDecodeError:
222
pass # ignore malformed features
223
224
# create ATProto record
225
atproto_uri = None
226
atproto_cid = None
···
243
features=featured_artists if featured_artists else None,
244
image_url=image_url,
245
)
246
-
if result:
247
-
atproto_uri, atproto_cid = result
248
except Exception as e:
249
-
logger.warning(
250
-
f"failed to create ATProto record: {e}", exc_info=True
251
-
)
252
253
# create track record
254
upload_tracker.update_status(
···
221
except json.JSONDecodeError:
222
pass # ignore malformed features
223
224
+
async def fail_atproto_sync(reason: str) -> None:
225
+
"""mark upload as failed when ATProto sync cannot complete."""
226
+
227
+
logger.error(
228
+
"upload %s failed during ATProto sync",
229
+
upload_id,
230
+
extra={
231
+
"file_id": file_id,
232
+
"artist_did": artist_did,
233
+
"reason": reason,
234
+
},
235
+
)
236
+
upload_tracker.update_status(
237
+
upload_id,
238
+
UploadStatus.FAILED,
239
+
"upload failed",
240
+
error=f"failed to sync track to ATProto: {reason}",
241
+
phase="atproto",
242
+
)
243
+
244
+
# delete uploaded media so we don't leave orphaned files behind
245
+
with contextlib.suppress(Exception):
246
+
await storage.delete(file_id, audio_format.value)
247
+
if image_id:
248
+
with contextlib.suppress(Exception):
249
+
await storage.delete(image_id)
250
+
251
# create ATProto record
252
atproto_uri = None
253
atproto_cid = None
···
270
features=featured_artists if featured_artists else None,
271
image_url=image_url,
272
)
273
+
if not result:
274
+
await fail_atproto_sync("PDS returned no record data")
275
+
return
276
+
atproto_uri, atproto_cid = result
277
except Exception as e:
278
+
await fail_atproto_sync(str(e))
279
+
return
280
+
else:
281
+
await fail_atproto_sync("no public audio URL available")
282
+
return
283
284
# create track record
285
upload_tracker.update_status(