AlistGo/alist · error
empty file id
Error message
empty file id
What it means
Streamtape's Link() extracts the numeric file ID from the object's composite ID using fileIDFromObjID, which strips an optional 'f:' prefix and returns the rest. It returns "" only when the stored object ID itself is empty, meaning the model.Obj passed in was constructed without an ID — the driver then cannot call /file/dlticket to mint a download ticket.
Source
Thrown at drivers/streamtape/driver.go:94
objects = append(objects, &model.Object{
ID: encodeFolderID(f.ID),
Name: f.Name,
IsFolder: true,
})
}
for _, f := range result.Files {
objects = append(objects, buildFileObj(f))
}
return objects, nil
}
func (d *Streamtape) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if file.IsDir() {
return nil, errs.NotFile
}
fileID := fileIDFromObjID(file.GetID())
if fileID == "" {
return nil, errors.New("empty file id")
}
var ticket dlTicketResult
if err := d.callAPI(ctx, "/file/dlticket", map[string]string{"file": fileID}, &ticket); err != nil {
return nil, err
}
var dl dlResult
waitSeconds := ticket.WaitTime
if waitSeconds > 0 {
timer := time.NewTimer(time.Duration(waitSeconds+1) * time.Second)
select {
case <-ctx.Done():
timer.Stop()
return nil, ctx.Err()
case <-timer.C:
}
}View on GitHub (pinned to 843d9dc814)
Solutions
- Always pass the exact model.Obj returned by the driver's List() to Link()
- If building objects manually, set ID via encodeFileID("<numeric streamtape file id>")
- Clear the listing cache for that path if a cached object lost its ID
Example fix
// before
file := &model.Object{Name: "video.mp4", Size: 123}
link, err := d.Link(ctx, file, args) // -> "empty file id"
// after
file := &model.Object{ID: encodeFileID("abc123XYZ"), Name: "video.mp4", Size: 123}
link, err := d.Link(ctx, file, args) Defensive patterns
Strategy: validation
Validate before calling
id := fileIDFromObjID(file.GetID())
if id == "" {
return fmt.Errorf("object %q has no streamtape file id; refresh listing and pass the object returned by List", file.GetName())
} Type guard
func hasStreamtapeFileID(o model.Obj) bool {
id := o.GetID()
return id != "" && fileIDFromObjID(id) != ""
} Try / catch
link, err := d.Link(ctx, file, args)
if err != nil && err.Error() == "empty file id" {
// re-list to get a well-formed object, then retry once
objs, lerr := d.List(ctx, parentDir, model.ListArgs{})
if lerr == nil {
for _, o := range objs {
if o.GetName() == file.GetName() && o.GetID() != "" {
link, err = d.Link(ctx, o, args)
}
}
}
} Prevention
- Always call Link with objects obtained from List()
- Never construct model.Object for driver calls without setting the ID
- Invalidate cached objects that lose their ID after re-serialization
When it happens
Trigger: Calling Link() with a hand-built model.Object (no ID set), or an object produced by a code path that returns model.URL wrappers or placeholder objects that never got an 'f:<id>' ID. Objects returned by List()/getFiles always carry encodeFileID and cannot trigger it.
Common situations: Custom scripts or extensions that synthesize an Obj instead of taking one from List(); Cache serving a stale/partial entry where the ID field was dropped; Renamed or copied objects routed through paths that lose the ID before Link
Related errors
- missing file id
- missing source file id
- streamtape move to root is not supported by API
- oss: chunkNum invalid
- chunkSize invalid
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/d7f32f128e92d6fc.
Report an issue: GitHub.